2010-02-01 26 views
10

tức là, những điều sau đây có thể được thực hiện đúng ngay cả trong môi trường đa luồng không?Trong C + +, là khởi tạo tĩnh của các kiểu nguyên thủy đến các giá trị không đổi thread-safe?

int dostuff(void) { 
    static int somevalue = 12345; 
    return somevalue; 
} 

Hoặc là nó có thể cho nhiều chủ đề để gọi này, và một cuộc gọi đến trả lại bất cứ rác là tại &somevalue trước khi thực hiện bắt đầu?

+0

const tĩnh không phải là một lựa chọn? –

+0

constness sẽ không hoạt động ở đây vì int sẽ được sửa đổi sau này (trong khi một mutex được biết là hợp lệ được giữ lại.) Cảm giác ruột của tôi là bất kỳ trình biên dịch sane nào sẽ tối thiểu bằng không khởi tạo các số nguyên tĩnh tại phạm vi hàm trước khi bắt đầu thực hiện (trong trường hợp đó, nó đủ tốt đối với tôi.) Tuy nhiên, đó là thứ dễ dàng để đi lên. –

+1

Về mặt kỹ thuật số. Nhưng gcc có một bản vá rõ ràng để đảm bảo rằng nó hoạt động trong môi trường đa luồng. –

Trả lời

2

Từ ++ Chuẩn C, phần 6.7:

Một đối tượng địa phương của POD loại (3.9) với thời hạn lưu trữ tĩnh khởi tạo với không đổi biểu thức được khởi tạo trước khi khối của nó là đầu tiên bước vào.

Điều này có nghĩa là đối tượng tĩnh cấp chức năng phải được khởi chạy lần đầu tiên khi chức năng được nhập, không nhất thiết khi toàn bộ quá trình được khởi tạo. Tại thời điểm này, nhiều luồng cũng có thể đang chạy.

+0

Agh, không thể thấy rằng trong tiêu chuẩn. Agh một lần nữa! Phần tương tự cho biết: "Không khởi tạo (8.5) của tất cả các đối tượng cục bộ với thời gian lưu trữ tĩnh (3.7.1) được thực hiện trước khi bất kỳ khởi tạo nào khác diễn ra." Vì vậy, nếu giá trị ban đầu là 0, giá trị của 0 nên (theo tiêu chuẩn) có giá trị ngay cả trong môi trường đa luồng, có? –

+0

Chắc chắn có vẻ như vậy. –

+0

Chỉ cần đảm bảo tôi đã đọc chính xác. Cảm ơn. :) –

4

Có, nó hoàn toàn an toàn (trên hầu hết các trình biên dịch). Tôi khuyên bạn nên ném vào điểm ngắt và xem cách thực hiện nhiệm vụ trên trình biên dịch cụ thể của bạn. Tôi không thể nói cho bạn biết bao nhiêu lần "tiêu chuẩn" bị vi phạm.

Nếu bạn chỉ định một địa phương tĩnh từ kết quả của một chức năng hoặc gọi phương thức, sau đó bạn có thể sẽ được đối phó với một điều kiện chủng tộc. Việc gán liên tục cho một kiểu nguyên thủy thường sẽ được tối ưu hóa.

On g ++ cho OS X 10.6.2, đây là mã máy tạo ra cho chức năng của bạn:

push rbp 
mov rbp,rsp 
lea rax,[rip+0x2067]  # 0x100003170 <_ZZ7dostuffvE9somevalue> 
mov eax,DWORD PTR [rax] 
leave 
ret

Như bạn thấy, không có chuyển nhượng. Trình biên dịch đã nướng nguyên thủy trong lúc xây dựng.

+0

+1 cho asm, mặc dù nó là trình biên dịch cụ thể :-) – Justicle

+0

Ngoài ra, tôi muốn xem ASM nếu someValue thực sự đã được sửa đổi trong hàm - Tôi nghĩ GCC đã thực hiện một số tối ưu hóa ở đây. Tôi có cảm giác mã kết quả sẽ giống như sau: http://stackoverflow.com/questions/2180501/in-c-are-static-initializations-of-primitive-types-to-constant-values-thread-s/ 2180547 # 2180547 – Justicle

+0

Khi tôi tăng giá trị, nó trông chính xác như trên, ngoại trừ ASM gia tăng. Việc đột biến giá trị sẽ giới thiệu một cuộc đua, nhưng không phải vì khởi tạo, mà vẫn còn vắng mặt. – pestilence669

2

Vì trình khởi tạo somevalue không yêu cầu gọi hàm tạo, điều này sẽ hoạt động tốt (một số thời gian sẽ được khởi tạo vào thời gian xây dựng).

Bây giờ, nếu bạn đã khởi tạo một giá trị mà yêu cầu một constructor:

void whatever() 
{ 
    static std::string value("bad"); 

    ... 
} 

Sau đó, bạn có thể gặp rắc rối với nhiều chủ đề. Bên trong, nội dung này sẽ được chuyển thành nội dung như:

void whatever() 
{ 
    static bool value_initialized = false; 
    static string_struct value; 

    if (!initialized) 
    { 
     construct_string(&value, "bad"); 
     value_initialized = false; 
    } 

    .... 
} 

Trong sự hiện diện của nhiều chủ đề, bạn có nhiều vấn đề khác nhau bao gồm điều kiện chủng tộc và khả năng hiển thị bộ nhớ).

10

Phần 6.7 của tiêu chuẩn đã cho biết:

Các zero-khởi của tất cả các đối tượng địa phương với thời gian lưu trữ tĩnh được thực hiện trước khi bất kỳ khởi khác diễn ra. Một đối tượng địa phương loại POD có lưu trữ tĩnh thời lượng được khởi tạo với biểu thức không đổi được khởi tạo trước khi khối của nó được nhập lần đầu tiên. An thực hiện được phép thực hiện khởi đầu của đối tượng ở địa phương khác với thời hạn lưu trữ tĩnh theo các điều kiện tương tự mà một thực hiện được phép tĩnh khởi tạo một đối tượng với thời gian lưu trữ tĩnh trong namespace phạm vi . Nếu không một đối tượng như vậy là khởi tạo điều khiển thời gian đầu tiên vượt qua tuyên bố của nó; như vậy một đối tượng được coi là khởi tạo khi hoàn thành việc khởi tạo của nó. Nếu khởi tạo thoát bằng cách ném ngoại lệ, việc khởi tạo không hoàn tất, do đó, sẽ được thử lại vào lần kiểm tra tiếp theo . Nếu kiểm soát, hãy nhập lại tờ khai (đệ quy) trong khi đối tượng là đang được khởi tạo, hành vi là không xác định.

Vì vậy, nếu đó là loại POD thì có vẻ như khởi tạo xảy ra khi khởi động trước khi có thể bắt đầu chuỗi mới. Đối với các loại không thuộc POD, nó phức tạp hơn, tiêu chuẩn nói rằng hành vi là không xác định (trừ khi ở một nơi khác nó nói một cái gì đó về an toàn luồng trong quá trình khởi tạo). Tôi biết rằng khi khởi tạo một đối tượng không POD, GCC lấy một mutex để ngăn chặn nó được khởi tạo hai lần (tôi biết điều này bởi vì tôi đã từng bế tắc một chương trình bằng cách vô tình đệ quy khởi tạo một đối tượng tĩnh).

Thật không may tôi không thể cho bạn biết nếu đây là trường hợp cho các trình biên dịch khác hoặc nó được yêu cầu ở nơi khác trong tiêu chuẩn.

+0

+1 để trích dẫn tiêu chuẩn – Justicle

+3

gcc giới thiệu chuỗi tĩnh an toàn trong dòng 4.x. –

+1

"Một đối tượng cục bộ của loại POD với thời gian lưu trữ tĩnh được khởi tạo với các biểu thức không đổi được khởi tạo trước khi khối của nó được nhập lần đầu tiên." tức là không phải lúc khởi động. –

0

từ kinh nghiệm của tôi hành vi của một tĩnh quy định tại phạm vi tập tin là khác nhau từ một tĩnh được định nghĩa trong một hàm

Phạm vi tập tin một cách an toàn được khởi tạo trước khi tất cả các chủ đề có được đi, phạm vi chức năng người ta không là. Một trong số ít nơi bạn không thể giữ nguyên quy tắc phạm vi tối thiểu. Lưu ý rằng điều này dường như phụ thuộc vào phiên bản trình biên dịch (mà bạn mong đợi cho rằng chúng tôi đang đi trong các khu vực hành vi 'không xác định')

Các vấn đề liên quan