2013-04-05 29 views
5

Tôi có hai chủ đề cố khóa cùng một số boost::mutex. Một trong những chủ đề đó liên tục xử lý một số dữ liệu và phần còn lại sẽ hiển thị định kỳ trạng thái hiện tại. Các chủ đề xử lý, theo ý định của tôi, phát hành khóa rất thường xuyên và reacquires nó, để các chủ đề hiển thị có thể gõ vào và có được nó bất cứ khi nào nó cần nó. Vì vậy, rõ ràng, tôi muốn các sợi hiển thị để có được khóa thời gian tiếp theo nó được phát hành bởi các chủ đề quá trình. Tuy nhiên, nó không làm điều đó, thay vào đó, nó chờ khóa và chỉ mua lại nó sau nhiều chu kỳ nhả khóa từ dây chuyền xử lý.Mua khóa ngay khi có sẵn

Hãy kiểm tra ví dụ nhỏ minh họa vấn đề của tôi:

#include <boost/thread.hpp> 
#include <iostream> 

using namespace std; 
using namespace boost; 

mutex mut; 

void process() { 
     double start = time(0); 
     while(1) { 
       unique_lock<mutex> lock(mut); 
       this_thread::sleep(posix_time::milliseconds(10)); 
       std::cout<<"."; 
       if(time(0)>start+10) break; 
     } 
} 

int main() { 

     thread t(process); 

     while(!t.timed_join(posix_time::seconds(1))) { 
       posix_time::ptime mst1 = posix_time::microsec_clock::local_time(); 
       cout<<endl<<"attempting to lock"<<endl; 
       cout.flush(); 

       unique_lock<mutex> lock(mut); 

       posix_time::ptime mst2 = posix_time::microsec_clock::local_time(); 
       posix_time::time_duration msdiff = mst2 - mst1; 
       cout << std::endl<<"acquired lock in: "<<msdiff.total_milliseconds() << endl; 
       cout.flush(); 
     } 

} 

Biên soạn với: g++ mutextest.cpp -lboost_thread -pthread

Khi tôi chạy file thực thi, một đầu ra mẫu là như thế này:

................................................................................................... 
attempting to lock 
.................................................................................................................................................................................................................................................................................................................................................................................................................................... 
acquired lock in: 4243 
................................................................................................... 
attempting to lock 
........................................................................................................ 
acquired lock in: 1049 
................................................................................................... 
attempting to lock 
........................................................................................................................ 
acquired lock in: 1211 
.................................... 

Như bạn có thể thấy, trong trường hợp xấu nhất, màn hình hiển thị chờ đợi cho 424 chu kỳ khóa-phát hành trước khi nó đến vòng để bắt khóa.

Tôi rõ ràng đang sử dụng mutex theo cách sai, nhưng cách thông thường để giải quyết vấn đề này là gì?

+0

bên cạnh nghi phạm thông thường: (biến điều kiện, năng suất,) thử một số container an toàn thread ... – NoSenseEtAl

Trả lời

0

Tôi đã giải quyết vấn đề bằng cách sử dụng các điều kiện, theo đề xuất của Dale Wilson một FKaria, nhưng tôi đã sử dụng nó theo hướng ngược lại. Vì vậy, quá trình kiểm tra chủ đề cho một lá cờ tạm dừng, và khi nó được thiết lập, nó chờ đợi một điều kiện, do đó phát hành khóa. Chuỗi hiển thị điều khiển cờ tạm dừng và nó cũng tiếp tục chuỗi quá trình bằng cách thông báo cho nó thông qua điều kiện. Mã số: (Mã này chủ yếu là giống nhau, tôi đã đánh dấu các dòng mới với //New)

#include <boost/thread.hpp> 
#include <boost/thread/condition.hpp> 
#include <iostream> 

using namespace std; 
using namespace boost; 

mutex mut; 
condition cond; 

volatile bool shouldPause = false; //New 

void process() { 
     double start = time(0); 
     while(1) { 
       unique_lock<mutex> lock(mut); 

       if(shouldPause) cond.wait(mut); //New 

       this_thread::sleep(posix_time::milliseconds(10)); 
       std::cout<<"."; 
       if(time(0)>start+10) break; 
     } 
} 

int main() { 

     thread t(process); 

     while(!t.timed_join(posix_time::seconds(1))) { 
       posix_time::ptime mst1 = posix_time::microsec_clock::local_time(); 
       cout<<endl<<"attempting to lock"<<endl; 
       cout.flush(); 
       shouldPause = true; // New 
       unique_lock<mutex> lock(mut); 

       posix_time::ptime mst2 = posix_time::microsec_clock::local_time(); 
       posix_time::time_duration msdiff = mst2 - mst1; 
       cout << std::endl<<"acquired lock in: "<<msdiff.total_milliseconds() << endl; 
       cout.flush(); 

       shouldPause = false; // New 
       cond.notify_all(); // New 
     } 

} 

Bây giờ đầu ra chính xác là như tôi muốn nó là:

................................................................................................... 
attempting to lock 
. 
acquired lock in: 9 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 8 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 9 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 8 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 9 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 9 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 9 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 9 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 8 
................................................................................................... 
attempting to lock 
. 
acquired lock in: 9 
.......................... 
4

Nó không phải là bạn đang sử dụng mutex sai, chỉ luồng đó không làm những gì bạn mong đợi ở đây. Hệ điều hành quyết định luồng nào chạy khi (điều này được gọi là "lập lịch biểu"), và không có gì trong mã buộc một công tắc luồng ở cuối vòng lặp; sợi chỉ tiếp tục và phản ứng lại khóa. Một điều cần thử là thêm một cuộc gọi đến this_thread::yield() sau khi nhả khóa (trong trường hợp này, ở đầu vòng lặp, trước khi đổ lại); điều đó sẽ gợi ý cho trình lên lịch biểu rằng nó thích hợp cho một luồng khác để chạy. Nếu bạn thực sự muốn xen kẽ đồng bộ chặt chẽ, chủ đề sẽ không làm điều đó; thay vào đó, hãy viết một hàm mức cao hơn để gọi hai hàm của bạn theo hàm kia.

+0

'yield()' dường như có phần giúp đỡ, nhưng nó vẫn còn xấu. Với 'yield()' trước khi xây dựng 'unique_lock' trong luồng xử lý, tôi vẫn thấy tối đa ~ 300 lần lặp phiên bản khóa trước khi chuỗi hiển thị bắt khóa. Tôi không thực sự muốn xen kẽ siêu chặt chẽ vì nó chỉ là một màn hình hiển thị, nhưng tôi sẽ đánh giá cao nếu nó không chờ đợi 300 lần lặp lại freaking :) – enobayram

+0

Yuk. Thử gọi 'ngủ'; khi một luồng ngủ, bất kỳ hệ điều hành hợp lý nào cũng sẽ chạy một số luồng khác. –

+0

Tôi cũng đã thử điều đó, nếu bạn ngủ trong 0 mili giây, hơn là giấc ngủ không tồn tại, nếu không bạn có thể ngủ ít nhất là 10 mili giây và chỉ đơn giản là lãng phí quá nhiều phiền toái như vậy: , nó không giống như tôi yêu cầu một lý thuyết không thể, việc thực hiện cấp khóa đơn giản chỉ cần đưa nó cho ứng cử viên sớm nhất. – enobayram

1

Nếu chuỗi cập nhật không có gì khác để làm điều đó, có thể chờ cho mutex khả dụng.

Xem xét tăng :: condition_variable. Bạn có thể đọc về nó ở đây http://www.boost.org/doc/libs/1_53_0/doc/html/thread/synchronization.html và đây Using boost condition variables

Nếu có thread cập nhật đi ngủ sẽ là một vấn đề - đó là trên nhiều hệ thống giao diện và bạn không xác định cái nào bạn đang sử dụng - xem xét việc đăng một thông điệp từ chuỗi xử lý đến cập nhật chuỗi. (một lần nữa các chi tiết phụ thuộc vào nền tảng của bạn.) Thông báo có thể chứa thông tin cần thiết để cập nhật hiển thị hoặc là thông báo rằng "bây giờ sẽ là thời điểm tốt để xem".

Nếu bạn sử dụng mã điều kiện, chuỗi xử lý có thể mang lại sau khi báo hiệu điều kiện và trước khi phản hồi khóa để mở một cửa sổ lớn cho chuỗi cập nhật.

+0

Lưu ý rằng chuỗi cập nhật của tôi đã đợi cho mutex khả dụng, nhưng vấn đề là nó không có khóa trong một thời gian rất dài ngay cả sau khi nó có sẵn. Nếu hệ thống GUI thực sự quan trọng, chuỗi hiển thị của tôi sử dụng OpenGL để thực hiện một số bản vẽ màn hình. – enobayram

+0

Chủ đề xử lý đăng thông báo lên chuỗi cập nhật sẽ không giúp ích gì cho bạn. Chuỗi hiển thị không thể "xem" mà không có khóa, và đó là nơi mà vấn đề là anyway. – enobayram

1

Bạn có thể muốn xem boost::condition_variable, đặc biệt tại các phương pháp wait()notify_one() hoặc notify_all(). Phương thức wait() sẽ chặn chuỗi hiện tại cho đến khi khóa được giải phóng. Phương thức notify_one()notify_all() thông báo cho một hoặc tất cả các chuỗi đang chờ khóa tiếp tục thực thi.

+0

Tôi đã thử 'condition_variable' nhưng nó không hoạt động. Các phương thức 'notify_x' đưa các luồng khác vào trạng thái" sẵn sàng ", nhưng chúng không bắt buộc đánh thức. Dù bằng cách nào, bạn cần khóa ở nơi đầu tiên trước khi bạn chờ đợi, vấn đề của tôi là không thể có được khóa. – enobayram

1

Như tôi thấy, nguyên tắc của mutexes không phải là sự công bằng, nhưng là đúng đắn. Bản thân các mutex không thể kiểm soát bộ lập lịch.Một điều làm phiền tôi là lý do tại sao bạn đã chọn tạo 2 chủ đề, sử dụng khóa thô như vậy. Về lý thuyết, bạn đang chạy hai thứ song song, nhưng thực ra bạn đang làm cho chúng phụ thuộc lẫn nhau.

Ý tưởng của Pete có vẻ đẹp hơn nhiều (một chức năng chạy các bản vẽ & cập nhật) và bạn vẫn có thể sử dụng các chủ đề bên trong mỗi hàm bên trong, không phải lo lắng về sự tranh chấp và công bằng.

Nếu bạn thực sự muốn có hai luồng chạy song song, sau đó tôi có thể cung cấp cho bạn chỉ là một vài lời khuyên:

  • spinlock sử dụng Atomics và quên đi mutex,
  • đi vào đất thực hiện xác định và đặt mức độ ưu tiên của luồng hoặc
  • làm cho khóa trở nên chi tiết hơn.

Rất tiếc, đây không phải là vấn đề.

+0

Cảm ơn các đề xuất của bạn, tất cả đều có trường hợp hữu ích, nhưng không phải của tôi: Một spinlock đơn giản là quá lãng phí, tôi muốn ở lại cầm tay và khóa là tốt hạt giống như nó về mặt lý thuyết có thể được. – enobayram

+0

Đối với việc tham gia chúng trong một chức năng duy nhất; Tôi có thể thấy rằng nó sẽ là cách tiếp cận tốt cho ví dụ tối thiểu, nhưng ứng dụng thực sự phức tạp hơn nhiều. bên cạnh đó, tôi cũng đang làm những việc khác trong chuỗi hiển thị. Tôi trích xuất các thông tin cần thiết từ mô hình thế giới và tôi giải phóng khóa trong khi chuỗi hiển thị xử lý các cuộc gọi OpenGL. – enobayram

+0

Cuối cùng, tôi cũng sử dụng mô hình đa luồng như một cách trừu tượng, nó thực sự đơn giản hóa thiết kế của tôi. – enobayram

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