2013-05-17 60 views
5

Tôi đang gặp vấn đề khi cố gắng đánh thức sợi chỉ bằng một cái khác. Một nhà sản xuất/điều tiêu dùng đơn giản.C++ 11 - không thể tỉnh táo một sợi chỉ bằng std :: thread và std :: condition_variable

Bên dưới mã. Dòng 85 là điểm tôi không hiểu tại sao nó không hoạt động. Chuỗi nhà sản xuất điền vào một hàng đợi :: std và gọi std :: condition_variable.notify_one() trong khi chuỗi tiêu thụ đang đợi NOT std :: queue.empty().

Cảm ơn trước sự giúp đỡ nào

#include <mutex> 
#include <condition_variable> 
#include <queue> 
#include <string> 
#include <iostream> 
#include <thread> 

// request 
class request : 
    public std::mutex, 
     public std::condition_variable, 
     public std::queue<std::string> 
{ 
public: 
    virtual ~request(); 
}; 

request::~request() 
{ 
} 

// producer 
class producer 
{ 
public: 
    producer(request &); 

    virtual ~producer(); 

    void operator()(); 

private: 
    request & request_; 
}; 

producer::producer(request & _request) 
: 
request_(_request) 
{ 
} 

producer::~producer() 
{ 
} 

void 
producer::operator()() 
{ 
    while (true) { 
    std::lock_guard<std::mutex> lock(request_); 
    std::cout << "producer\n"; 
    request_.push("something"); 
    std::this_thread::sleep_for(std::chrono::seconds(1)); 
    request_.notify_one(); 
    } 
} 

class consumer 
{ 
public: 
    consumer(request &); 

    virtual ~consumer(); 

    void operator()(); 

private: 
    request & request_; 
}; 

consumer::consumer(request & _request) 
: 
request_(_request) 
{ 
} 

consumer::~consumer() 
{ 
} 

void 
consumer::operator()() 
{ 
    while (true) { 
    std::unique_lock<std::mutex> lock(request_); // <-- the problem 
    std::cout << "consumer\n"; 
    request_.wait (
     lock, [this] {return !request_.empty();} 
    ); 
    request_.pop(); 
    } 
} 

int 
main() 
{ 
    // request 
    request request_; 

    // producer 
    std::thread producer_{producer(request_)}; 

    // consumer 
    std::thread first_consumer_{consumer(request_)}; 
    std::thread second_consumer_{consumer(request_)}; 

    // join 
    producer_.join(); 
    first_consumer_.join(); 
    second_consumer_.join(); 
} 
+8

điên thừa kế – fasked

+0

tại sao không? bạn có thể cho thấy một ví dụ ngắn gọn như thế nào? – user1587451

+1

'std :: thread_self :: yield()' có thể giúp sau 'notify_xxx' –

Trả lời

0

Bạn cần phải mở khóa std::unique_lock của bạn trước khi gọi notify_one() khác vòng lặp while của bạn sẽ cố gắng để khóa hai lần trong cùng một thread. Điều này là hợp lệ cho cả nhà sản xuất và người tiêu dùng.

Tuy nhiên, tôi đồng ý với những người cho rằng đạo hàm của bạn theo yêu cầu rất gây hiểu nhầm. Bạn nên sử dụng bố cục. Nếu bạn cho tôi 10 phút, tôi có thể đưa ra một cái gì đó mà làm việc :)

+2

Không cần phải mở khóa trước khi thông báo. Có, tuy nhiên, cần phải mở khóa ** một nơi nào đó ** để các thread khác có cơ hội tại mutex. –

+0

Trên đầu trang, những gì @ user1587451 muốn làm là ở đây đã có: http://en.cppreference.com/w/cpp/thread/condition_variable – davideanastasia

+0

@davideanastasia để biết thông tin, ví dụ này tạo ra UB http://stackoverflow.com/questions/15733887/understand-the-example-of-using-stdcondition-biến – fasked

11

Sửa code dưới đây, với những thay đổi này:

  • Đừng bắt nguồn từ các mutex, condvar và xếp hàng như thế, thật kinh khủng.
  • Mở khóa mutex NHƯ S SO CÓ THỂ CÓ THỂ sau khi thêm mục vào hàng đợi, các phần quan trọng phải luôn nhỏ nhất có thể. Điều này cho phép người tiêu dùng thức dậy trong khi nhà sản xuất đang ngủ.
  • Tuôn ra cout (Tôi đã sử dụng endl để thực hiện điều đó) để đầu ra được in ngay lập tức, điều này giúp dễ dàng hơn để xem điều gì đang xảy ra.
  • In "consumer"sau thức dậy, bởi vì đó là khi người tiêu dùng tiêu thụ, nếu không bạn sẽ nhận được đầu ra gây hiểu lầm cho thấy khi người tiêu dùng đang ngủ, không phải khi nó hoạt động.

Vấn đề chính với mã của bạn là nhà sản xuất không bao giờ cho người tiêu dùng cơ hội để chạy. Nó được thêm vào hàng đợi, ngủ trong một giây (vẫn giữ khóa mutex) rồi thông báo biến điều kiện (vẫn giữ mutex), sau đó thực sự nhanh chóng nhả khóa mutex và lấy lại nó. Có lẽ những gì bạn thấy là một sợi người tiêu dùng đã nhận được thông báo, cố gắng để có được khóa mutex, tìm thấy nó vẫn bị khóa (bởi các chủ đề sản xuất) và do đó đã trở lại giấc ngủ. Các nhà sản xuất không bao giờ phát hành mutex đủ dài cho một sợi khác để có được nó. Bạn có thể có thể nhận được kết quả tốt hơn bằng cách thêm std::this_thread::yield() vào đầu vòng lặp của nhà sản xuất, trước khi khóa mutex, nhưng các thuật toán dựa trên yield() cho tính đúng đắn thường bị hỏng (và thực sự nó không có sự khác biệt trong kiểm tra); tốt hơn là sửa vòng lặp của nhà sản xuất để cung cấp cho người tiêu dùng cơ hội thức dậy và chạy.

Dưới đây là đoạn code làm việc:

#include <mutex> 
#include <condition_variable> 
#include <queue> 
#include <string> 
#include <iostream> 
#include <thread> 

// request 
struct request 
{ 
    std::mutex mx; 
    std::condition_variable cv; 
    std::queue<std::string> q; 
}; 

// producer 
class producer 
{ 
public: 
    producer(request & r) : request_(r) { } 

    void operator()(); 

private: 
    request & request_; 
}; 

void 
producer::operator()() 
{ 
    while (true) { 
     { 
      std::lock_guard<std::mutex> lock(request_.mx); 
      std::cout << "producer" << std::endl; 
      request_.q.push("something"); 
     } 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 
     request_.cv.notify_one(); 
    } 
} 

class consumer 
{ 
public: 
    consumer(request & r) : request_(r) { } 

    void operator()(); 

private: 
    request & request_; 
}; 

void 
consumer::operator()() 
{ 
    while (true) { 
    std::unique_lock<std::mutex> lock(request_.mx); 
    request_.cv.wait (
     lock, [this] {return !request_.q.empty();} 
    ); 
    std::cout << "consumer" << std::endl; 
    request_.q.pop(); 
    } 
} 

int 
main() 
{ 
    // request 
    request request_; 

    // producer 
    std::thread producer_{producer(request_)}; 

    // consumer 
    std::thread first_consumer_{consumer(request_)}; 
    std::thread second_consumer_{consumer(request_)}; 

    // join 
    producer_.join(); 
    first_consumer_.join(); 
    second_consumer_.join(); 
} 
+0

Cảm ơn một tấn cho rằng desription chi tiết . Tôi hiểu thất bại của tôi. – user1587451

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