Các biến thread_local trong C ++ 11 có tự động tĩnh không?


85

Có sự khác biệt giữa hai đoạn mã này không:

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Thông tin cơ bản: ban đầu tôi có một vectơ STATIC V (để giữ một số giá trị trung gian, nó sẽ bị xóa mỗi khi tôi nhập hàm) và một chương trình đơn luồng. Tôi muốn biến chương trình thành một chương trình đa luồng, vì vậy bằng cách nào đó tôi phải loại bỏ công cụ sửa đổi tĩnh này. Ý tưởng của tôi là biến mọi tĩnh thành thread_local và không phải lo lắng về bất cứ điều gì khác? Cách tiếp cận này có thể phản tác dụng không?


16
Có một thread_localbiến cục bộ không có ý nghĩa gì khi bắt đầu… mỗi luồng có ngăn xếp cuộc gọi riêng của nó.
Konrad Rudolph

1
Một số hàm C ban đầu được viết để trả về địa chỉ của các biến tĩnh hoặc toàn cục. Điều này sau đó được phát hiện là dẫn đến các lỗi khó hiểu khi được sử dụng trong các ứng dụng đa luồng (ví dụ: errno, localtime). Ngoài ra, đôi khi sẽ rất bất lợi khi bảo vệ các biến được chia sẻ bằng mutex khi một hàm đang được gọi từ nhiều luồng hoặc phải chuyển một đối tượng ngữ cảnh luồng giữa nhiều đối tượng và phương thức gọi. Các biến cục bộ đối với một luồng giải quyết những vấn đề này và những vấn đề khác.
edwinc

3
@Konrad Rudolph chỉ khai báo các biến cục bộ staticthay vì static thread_localkhông khởi tạo một phiên bản của biến cho mỗi luồng.
davide

1
@davide Đó không phải là vấn đề, của tôi hay OP. Chúng tôi không nói về staticvs static thread_localmà là về autovs thread_local, sử dụng nghĩa trước C ++ 11 của auto(tức là lưu trữ tự động).
Konrad Rudolph

1
Ngoài ra, hãy xem Cách xác định biến tĩnh cục bộ của chuỗi-cục bộ? . Lưu ý nhanh của luật sư về ngôn ngữ ... Hỗ trợ của Microsoft và TLS đã thay đổi trong Vista; xem Lưu trữ cục bộ chủ đề (TLS) . Thay đổi ảnh hưởng đến những thứ như Singleton's và có thể áp dụng hoặc không. Nếu bạn đang sử dụng mô hình phần mềm phần mềm gốc thì bạn có thể sẽ ổn. Nếu bạn sẵn lòng hỗ trợ nhiều trình biên dịch và nền tảng thì bạn có thể phải chú ý đến nó.
jww

Câu trả lời:


92

Theo tiêu chuẩn C ++

Khi thread_local được áp dụng cho một biến của phạm vi khối, tĩnh lưu trữ-class-specifier được ngụ ý nếu nó không xuất hiện rõ ràng

Vì vậy, nó có nghĩa là định nghĩa này

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

tương đương với

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Tuy nhiên, một biến static không giống như một biến thread_local.

1 Tất cả các biến được khai báo với từ khóa thread_local đều có thời lượng lưu trữ luồng. Việc lưu trữ cho các thực thể này sẽ kéo dài trong suốt thời gian của chuỗi mà chúng được tạo. Có một đối tượng hoặc tham chiếu riêng biệt cho mỗi luồng và việc sử dụng tên đã khai báo đề cập đến thực thể được liên kết với luồng hiện tại

Để phân biệt các biến này, tiêu chuẩn giới thiệu thời lượng lưu trữ luồng thuật ngữ mới cùng với thời lượng lưu trữ tĩnh.


1
staticexterndo đó không ngụ ý lớp lưu trữ mà chỉ liên kết cho các biến thread_local ngay cả trong phạm vi bên trong.
Deduplicator

4
@Deduplicator Các biến phạm vi khối không có liên kết. Vì vậy, sơ yếu lý lịch của bạn là sai. Như tôi đã viết trong bài, chúng có thời lượng lưu trữ luồng. Trên thực tế, nó giống như thời lượng lưu trữ tĩnh nhưng được áp dụng cho từng luồng.
Vlad từ Moscow

1
Nếu bạn thêm dấu ngoặc kép, bạn đang thực hiện một khai báo không phải là một định nghĩa. Vì thế?
Deduplicator

1
@Deduplicator Vì vậy, nó có nghĩa là các định nghĩa của các biến phạm vi khối không có liên kết.
Vlad từ Moscow

1
Tôi vừa thử điều đó trong VS 2013 và nó kêu "biến TL không thể được khởi tạo động". Tôi đang phân vân.
v.oddou

19

Có, "lưu trữ cục bộ luồng" rất giống với "toàn cầu" (hoặc "lưu trữ tĩnh"), chỉ khác là thay vì "thời lượng của toàn bộ chương trình", bạn có "thời lượng của toàn bộ luồng". Vì vậy, một biến khối-cục bộ luồng-cục bộ được khởi tạo lần đầu tiên điều khiển đi qua khai báo của nó, nhưng riêng biệt trong mỗi luồng và nó bị hủy khi luồng kết thúc.


6

Khi được sử dụng với thread_local, staticđược ngụ ý trong phạm vi khối (xem câu trả lời của @ Vlad), được yêu cầu đối với một thành viên trong lớp; Tôi đoán, có nghĩa là liên kết cho phạm vi không gian tên.

Mỗi 9,2 / 6:

Trong định nghĩa lớp, một thành viên sẽ không được khai báo với thread_local storage-class-specifier trừ khi cũng được khai báo static

Để trả lời câu hỏi ban đầu:

Các biến thread_local trong C ++ 11 có tự động tĩnh không?

Không có lựa chọn nào khác, ngoại trừ các biến phạm vi không gian tên.

Có sự khác biệt giữa hai đoạn mã này không:

Không.


4

Lưu trữ cục bộ luồng là tĩnh nhưng nó hoạt động khá khác với lưu trữ tĩnh đơn giản.

Khi bạn khai báo một biến static, sẽ có đúng một phiên bản của biến. Hệ thống biên dịch / thời gian chạy đảm bảo rằng nó sẽ được khởi tạo cho bạn vào một lúc nào đó trước khi bạn thực sự sử dụng nó, mà không chỉ định chính xác khi nào (một số chi tiết được bỏ qua ở đây.)

C ++ 11 đảm bảo rằng quá trình khởi tạo này sẽ an toàn cho luồng, tuy nhiên trước C ++ 11, an toàn cho luồng này không được đảm bảo. Ví dụ

static X * pointer = new X;

có thể làm rò rỉ các phiên bản của X nếu nhiều luồng truy cập mã khởi tạo tĩnh cùng một lúc.

Khi bạn khai báo một biến địa phương luồng có thể có nhiều trường hợp của biến. Bạn có thể nghĩ chúng nằm trong một bản đồ được lập chỉ mục bởi thread-id. Điều đó có nghĩa là mỗi luồng nhìn thấy bản sao biến của chính nó.

Một lần nữa, nếu biến được khởi tạo, hệ thống biên dịch / thời gian chạy đảm bảo rằng quá trình khởi tạo này sẽ diễn ra trước khi dữ liệu được sử dụng và quá trình khởi tạo sẽ diễn ra cho mỗi luồng sử dụng biến. Trình biên dịch cũng đảm bảo rằng quá trình khởi tạo sẽ an toàn cho luồng.

Đảm bảo an toàn cho luồng có nghĩa là có thể có khá nhiều mã hậu trường để làm cho biến hoạt động theo cách bạn mong đợi - đặc biệt là khi xem xét rằng trình biên dịch không có cách nào biết trước chính xác bao nhiêu luồng sẽ tồn tại trong chương trình của bạn và bao nhiêu trong số này sẽ chạm vào biến cục bộ của luồng.


@Etherealone: ​​Thật thú vị. Thông tin cụ thể nào? Bạn có thể cung cấp một tài liệu tham khảo?
Dale Wilson

1
stackoverflow.com/a/8102145/1576556 . Wikipedia C ++ 11 bài viết đề cập đến nó nếu tôi nhớ đúng.
Etherealone

1
Tuy nhiên, các đối tượng tĩnh được khởi tạo đầu tiên, sau đó gán bản sao. Vì vậy, tôi cũng hơi không rõ liệu khởi tạo an toàn luồng có bao gồm biểu thức đầy đủ hay không. Nó có thể làm được mặc dù vì nếu không nó sẽ không được coi là an toàn cho luồng.
Etherealone
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.