2010-07-26 31 views
7

Tôi có các đối tượng hàm nhỏ nhưng thường được sử dụng. Mỗi luồng có bản sao riêng. Mọi thứ được phân bổ tĩnh. Bản sao không chia sẻ bất kỳ dữ liệu toàn cầu hoặc tĩnh nào. Tôi có cần phải bảo vệ các đối tượng này khỏi chia sẻ sai không?Các biến chia sẻ và ngăn xếp sai

Cảm ơn bạn. EDIT: Đây là một chương trình đồ chơi sử dụng Boost.Threads. Có thể chia sẻ sai cho trường dữ liệu không?

#include <boost/thread/thread.hpp> 

struct Work { 
    void operator()() { 
     ++data; 
    } 

    int data; 
}; 

int main() { 
    boost::thread_group threads; 
    for (int i = 0; i < 10; ++i) 
     threads.create_thread(Work()); 
    threads.join_all(); 
} 
+0

Mã sẽ hoạt động tốt hơn. Nếu các đối tượng hàm của bạn có dữ liệu 'tĩnh', thì tất cả các luồng sẽ chia sẻ dữ liệu đó. – GManNickG

+0

Hãy suy nghĩ bạn cần phải nói chính xác những gì bạn có nghĩa là "mỗi thread được bản sao riêng của nó" và "phân bổ tĩnh". Các chủ đề có sử dụng lẫn nhau không? – Elemental

+0

@Elemental: Một số trình biên dịch có thể sử dụng bộ nhớ cục bộ TLS-thread. Điều này có nghĩa rằng bạn có thể phân bổ tĩnh và thread-an toàn, mặc dù như vậy là chậm. – Puppy

Trả lời

6

Chia sẻ sai giữa các chuỗi là khi 2 hoặc nhiều chủ đề sử dụng cùng một dòng bộ nhớ cache.

Ví dụ: :

struct Work { 
    Work(int& d) : data(d) {} 
    void operator()() { 
     ++data; 
    } 

    int& data; 
}; 

int main() { 
    int false_sharing[10] = { 0 }; 
    boost::thread_group threads; 
    for (int i = 0; i < 10; ++i) 
     threads.create_thread(Work(false_sharing[i])); 
    threads.join_all(); 

    int no_false_sharing[10 * CACHELINE_SIZE_INTS] = { 0 }; 
    for (int i = 0; i < 10; ++i) 
     threads.create_thread(Work(no_false_sharing[i * CACHELINE_SIZE_INTS])); 
    threads.join_all(); 
} 

Chủ đề trong khối đầu tiên bị chia sẻ sai. Các chủ đề trong khối thứ hai không (nhờ CACHELINE_SIZE).

Dữ liệu trên ngăn xếp luôn cách 'xa' các chủ đề khác. (Ví dụ: dưới cửa sổ, ít nhất một vài trang).

Với định nghĩa của đối tượng hàm, chia sẻ sai có thể xuất hiện, vì các trường hợp Work được tạo trên vùng heap và không gian vùng này được sử dụng bên trong chuỗi.

Điều này có thể dẫn đến một số trường hợp Work liền kề và do đó có thể phải chia sẻ các dòng bộ nhớ cache.

Nhưng ... mẫu của bạn không có ý nghĩa, bởi vì dữ liệu không bao giờ được chạm vào bên ngoài và do đó chia sẻ sai được gây ra không cần thiết.

Cách dễ nhất, để ngăn chặn các vấn đề như thế này, là sao chép dữ liệu 'được chia sẻ' cục bộ trên tho ngăn xếp và sau đó làm việc trên bản sao ngăn xếp. Khi công việc của bạn hoàn tất, hãy sao chép nó trở lại đầu ra var.

ví dụ:

struct Work { 
    Work(int& d) : data(d) {} 
    void operator()() 
    { 
     int tmp = data; 
     for(int i = 0; i < lengthy_op; ++i) 
      ++tmp; 
     data = tmp; 
    } 

    int& data; 
}; 

Điều này ngăn cản tất cả các vấn đề với chia sẻ.

+0

Bạn có nói rằng dữ liệu có thể bị ảnh hưởng bởi chia sẻ sai không? Trong trường hợp của tôi, việc sao chép nó vào ngăn xếp của hàm sẽ không giúp ích được, bởi vì bản thân hàm phải được gọi thường xuyên và chỉ sử dụng dữ liệu một lần cho mỗi cuộc gọi. – user401947

+0

Khi chức năng phải được gọi rất thường xuyên, nó không có ý nghĩa để tạo ra một chủ đề mỗi lần. Hoặc bạn làm nhiều công việc trong một chủ đề mới, hoặc bạn chỉ cần ghi chu kỳ để tạo/hủy luồng. Và trong trường hợp sau này, bạn làm lu mờ chi phí chia sẻ sai lệch bởi các chi phí khổng lồ của chủ đề. – Christopher

+0

Tuy nhiên. Nếu bạn không thể sao chép dữ liệu vào ngăn xếp cho hoạt động của mình, thì chỉ cần thực hiện 'Công việc' đủ lớn để dài ít nhất CACHLINE_SIZE. Bạn mất một vài byte, nhưng bạn thực sự có thể chắc chắn không bao giờ chạy vào các vấn đề chia sẻ sai. – Christopher

0

Tôi không cảm thấy hoàn toàn an toàn với các chi tiết, nhưng đây là quan điểm của tôi:

(1) ví dụ đơn giản của bạn bị hỏng từ tăng create_thread hy vọng một tài liệu tham khảo, bạn vượt qua một tạm thời.

(2) nếu bạn sử dụng vector<Work> với một mục cho mỗi chủ đề hoặc giả sử chúng có trong bộ nhớ theo tuần tự, chia sẻ sai sẽ xảy ra.

+0

(1) Không, nó không bị hỏng. create_thread chấp nhận đối số của nó theo giá trị. Kiểm tra tờ khai nếu bạn không tin tôi. (2) Tôi đã tuyên bố rõ ràng rằng mỗi sợi đều có bản sao riêng. Kiểm tra mã. Đối tượng hàm được truyền theo giá trị. – user401947

+0

Đó là. Công việc không được sao chép vào ngăn xếp đích. Nó được 'làm mới' trong ngữ cảnh 'create_thread' và chỉ một con trỏ (được chia sẻ-) được chuyển vào ngăn xếp đích. Có dữ liệu chỉ được tham chiếu bởi một con trỏ. (Tôi đã thử nghiệm điều này, bằng cách gán thread_id cho thành viên dữ liệu, và sau đó nhìn vào giá trị trong toán tử().) – Christopher

+0

(1) chúng ta đang nói về: 'thread * create_thread (const boost :: function0 & threadfunc); Đó là những gì tôi tìm thấy khi cố gắng kiểm tra các tài liệu tham khảo – peterchen

2

Tôi đã nghiên cứu một chút công bằng và có vẻ như không có giải pháp viên đạn bạc nào để chia sẻ sai. Đây là những gì tôi đưa ra (nhờ Christopher): 1) Pad dữ liệu của bạn từ cả hai bên với công cụ không sử dụng hoặc ít được sử dụng thường xuyên. 2) Sao chép dữ liệu của bạn vào ngăn xếp và sao chép lại sau khi tất cả công việc khó khăn được thực hiện. 3) Sử dụng cấp phát bộ nhớ liên kết bộ nhớ cache.

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