2009-03-22 23 views
5

Tôi đang làm việc trên một dự án và cố gắng sử dụng pthread_cond_wait()pthread_cond_signal() để đồng bộ hóa hai luồng.Tín hiệu có điều kiện Pthread - không hoạt động như mong đợi

Mã của tôi trông giống như sau:

pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t write_it = PTHREAD_COND_INITIALIZER; 

    int main(int argc, char**argv) 
    { 

    pthread_t t_send_segments, t_recv_acks; 

    pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL); 
    pthread_create(&t_recv_acks,  NULL, recv_acks,  (void*)NULL); 

    pthread_join(t_recv_acks, (void**)NULL); 

    pthread_mutex_destroy(&lock_it); 
    pthread_cond_destroy(&write_it); 
} 

void* send_segments(void *v) { 
    for(;;) { 
     pthread_mutex_lock(&lock_it); 
     printf("s1\n"); 
     printf("s2\n"); 
     pthread_cond_wait(&write_it, &lock_it); 
     printf("s3\n"); 
     printf("s4\n"); 
     printf("s5\n"); 
     pthread_mutex_unlock(&lock_it); 
    } 
    return 0; 
} 

void* recv_acks(void *v) { 
    for(;;) { 
     pthread_mutex_lock(&lock_it); 
     printf("r1\n"); 
     pthread_cond_signal(&write_it); 
     printf("r2\n"); 
     pthread_mutex_unlock(&lock_it); 
    } 
    return 0; 
} 

Sản lượng dự kiến ​​là:

s1 
s2 
r1 
s3 
s4 
s5 
s1 
s2 
r2 
r1 
s3 
s4 
s5 

(etc) 

sản lượng của tôi không làm theo mô hình này ở tất cả. Rõ ràng là tôi có lỗi logic ở đâu đó, nhưng tôi không hiểu đâu. Tại sao sản phẩm recv_acks() chỉ có hiệu suất là khi nó chạm vào số pthread_cond_signal() - vì pthread_cond_wait() luôn thực hiện trước tiên (vì thứ tự mà tôi tạo chủ đề) và cond_wait() luôn thực thi vì nó nằm trong phần quan trọng?

+0

đầu ra của bạn trông như thế nào? – Hasturkun

+0

"luôn luôn thực hiện đầu tiên (vì thứ tự mà tôi tạo ra các chủ đề" - dựa vào điều này có thể khá nguy hiểm. Hãy tắt mutex trước khi bắt đầu chủ đề và chuyển quyền sở hữu cho người gửi. – Dipstick

Trả lời

8

Các pthread_cond_signal chức năng không gây ra các thread hiện tại để mang lại và không phát hành mutex. Tất cả những gì nó làm là khởi động lại một luồng đã tự treo trên điều kiện qua pthread_cond_wait. Điều này chỉ có nghĩa là thread được đánh thức có sẵn để lên lịch, nó không làm cho nó thực thi ngay lập tức. Trình lập lịch trình chuỗi sẽ lên lịch trong tương lai.

Ngoài ra, chỉ vì s-thread đã được đánh thức và đang cạnh tranh với mutex, điều đó không có nghĩa là nó sẽ nhận được mutex tiếp theo. Mutexes không nhất thiết phải công bằng đối với tất cả các chủ đề đã yêu cầu. Theo trang pthread_mutex người đàn ông: "pthread_mutex_lock khóa mutex đã cho. Nếu mutex hiện đang được mở khóa, nó sẽ bị khóa và thuộc sở hữu của chuỗi cuộc gọi, và pthread_mutex_lock sẽ trả về ngay lập tức." Vì vậy, các r-thread có thể quay trong vòng lặp của nó nhiều lần, vui vẻ mở khóa và relocking mutex nhiều lần trước khi được trao đổi bởi các Scheduler. Điều này có nghĩa là s-thread sẽ chỉ nhận được một cơ hội tại mutex nếu scheduler xảy ra để làm gián đoạn r-thread trong thời gian ngắn mà nó đã phát hành mutex.

Để đạt được kết quả bạn muốn, cả hai luồng sẽ cần kiểm soát việc thực thi của chúng với điều kiện và báo hiệu cho nhau trước khi tự treo. Tuy nhiên, điều này có thể hoặc không thể là những gì bạn thực sự muốn làm với dự án thực sự của bạn.

Lưu ý khác: nó không thực sự quan trọng đối với thứ tự bạn đã tạo chủ đề. Tạo chủ đề không tạo ra chuỗi tạo. Vì vậy, các chủ đề chính có thể sẽ tạo ra cả hai chủ đề trước khi hoặc được lên kế hoạch, và lập lịch trình thread là miễn phí để sắp xếp hoặc là một trong số họ để thực hiện tiếp theo. Nếu s-thread chạy đầu tiên trên nền tảng của bạn, điều đó chỉ xảy ra là hành vi triển khai trên nền tảng của bạn và không phải là một cái gì đó nên được dựa vào.

6

Tôi dường như nhớ đọc ở đâu đó rằng pthread_cond_signal() không thực sự khiến cho luồng tạo ra ngay lập tức. Vì pthread_cond_signal() không giải phóng khóa, chủ đề gọi nó phải tiếp tục thực thi cho đến khi nó giải phóng khóa và chỉ sau đó nó sẽ sinh ra và cho phép chuỗi được báo hiệu trả về từ pthread_cond_wait().

Nếu đó là trường hợp, sau đó tôi nghĩ rằng đầu ra của bạn sẽ trông như thế

s1 
s2 
r1 
r2 
s3 
s4 
s5 

vv .... nếu nó không phải là, bạn có thể cần phải chỉnh sửa đầu ra thực tế vào câu hỏi của bạn để có được một câu trả lời chính xác .

1

Tôi nghĩ rằng sự cố của bạn đến từ việc cố gắng sử dụng một khóa cho quá nhiều thứ. Bạn chỉ nên sử dụng một khóa cho 1 điều, theo cách đó không có sự nhầm lẫn về những gì bạn đang chờ đợi. Tôi đề nghị thêm một khóa thứ hai cho tín hiệu ghi. Ngoài ra, bạn nên thêm cond_wait thứ hai cho nhóm thư thứ hai. Nếu bạn không, thứ tự mà mọi thứ chạy sẽ là ngẫu nhiên. Đây là phiên bản của tôi thay đổi nội dung chương trình của bạn:

#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 

#define NUM_MESSAGES 3 
int messages = 0; 
void * send_segments(void *); 
void * recv_acks(void *v); 
pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER; 
pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER; 

    int main(int argc, char**argv) 
    { 

    pthread_t t_send_segments, t_recv_acks; 

    pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL); 
    pthread_create(&t_recv_acks,  NULL, recv_acks,  (void*)NULL); 

    pthread_join(t_recv_acks, (void**)NULL); 
    pthread_join(t_send_segments, (void**)NULL); 

    //pthread_mutex_destroy(&lock_it); 
    //pthread_cond_destroy(&write_it); 
} 

void* send_segments(void *v) { 
    for(;;) { 
     pthread_mutex_lock(&lock_it); 
     printf("s1\n"); 
     printf("s2\n"); 
     pthread_mutex_unlock(&lock_it); 

     // wait for other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_wait(&write_cond, &write_mutex); 
     pthread_mutex_unlock(&write_mutex); 

     pthread_mutex_lock(&lock_it); 
     printf("s3\n"); 
     printf("s4\n"); 
     printf("s5\n"); 
     pthread_mutex_unlock(&lock_it); 

     // wait for other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_wait(&write_cond, &write_mutex); 
     pthread_mutex_unlock(&write_mutex); 

     if (messages > NUM_MESSAGES) return(NULL); 
    } 
    return 0; 
} 


void* recv_acks(void *v) { 
    for(;;) { 
     // write first response 
     pthread_mutex_lock(&lock_it); 
     printf("r1\n"); 
     pthread_mutex_unlock(&lock_it); 

     // signal other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_signal(&write_cond); 
     pthread_mutex_unlock(&write_mutex); 

     // write second response 
     pthread_mutex_lock(&lock_it); 
     printf("r2\n\n"); 
     // increment count before releasing lock, otherwise the other thread 
     // will be stuck waiting for a write_cond signal 
     messages++; 
     pthread_mutex_unlock(&lock_it); 

     // signal other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_signal(&write_cond); 
     pthread_mutex_unlock(&write_mutex); 

     if (messages > NUM_MESSAGES) return(NULL); 
    } 
    return 0; 
} 

Nếu tôi chạy chương trình này, tôi nhận được kết quả như sau (lưu ý rằng tôi đã thêm một dòng mới thứ hai sau khi r2 cho rõ ràng):

[email protected]:~/tmp$ ./test 
s1 
s2 
r1 
s3 
s4 
s5 
r2 

s1 
s2 
r1 
s3 
s4 
s5 
r2 

s1 
s2 
r1 
s3 
s4 
s5 
r2 

s1 
s2 
r1 
s3 
s4 
s5 
r2 

[email protected]:~/tmp$ 
+0

Tôi chạy mã này bạn đã đăng và không Có vẻ như các chủ đề nhận được của tôi được lên lịch trước và chương trình chỉ bị treo: [ubuntu @ apollo: ~/hp_threads] $ gcc -lpthread main.c && ./a.out r1 r2 r1 r2 r1 r2 r1 r2 s1 s2 ^ C – RishiD

4

Bạn đang sử dụng sai điều kiện. Tôi không hiểu chính xác những gì bạn muốn nhưng những gì bạn đang cố gắng làm trông giống như một vấn đề đồng bộ hóa điển hình được gọi là vấn đề sản xuất/người tiêu dùng có thể được thực hiện với các điều kiện như tôi đã làm sau.

Bạn nên có cái nhìn về điều này để hiểu các điều kiện được sử dụng như thế nào. Vấn đề là như sau: Tôi có hai luồng, một ghi dữ liệu vào bộ đệm có kích thước cố định và một dữ liệu đọc khác từ bộ đệm. Luồng đầu tiên không thể ghi thêm dữ liệu nếu bộ đệm đầy, thứ hai không thể đọc nếu bộ đệm trống.

#include <stdlib.h> 
#include <stdio.h> 

#include <pthread.h> 

#define BUFFER_SIZE 4 

int buffer_nb_entries = 0; 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 

void send(){ 
for(;;){ 
    pthread_mutex_lock(&mutex); 
    while(buffer_nb_entries == BUFFER_SIZE) /* If buffer is full, then wait */ 
    pthread_cond_wait(&cond, &mutex); 

    /* Here I am sure that buffer is not full */ 
    printf("sending\n"); 
    buffer_nb_entries++; 

    pthread_cond_signal(&cond); // signal that the condition has changed. 
    pthread_mutex_unlock(&mutex); 
} 
} 

void receive(){ 
    for(;;){ 
    pthread_mutex_lock(&mutex); 
    while(buffer_nb_entries == 0) 
     pthread_cond_wait(&cond, &mutex); 
    /* Here I am sure that buffer is not empty */ 
    printf("receiving\n"); 
    buffer_nb_entries--; 
    pthread_cond_signal(&cond); 
    pthread_mutex_unlock(&mutex); 
    } 

} 

int main(){ 
    pthread_t s, r; 

    pthread_create(&s, NULL, (void *(*)(void*))send, NULL); 
    pthread_create(&r, NULL, (void *(*)(void*))receive, NULL); 

    pthread_join(s, NULL); 

} 

Lưu ý rằng bạn phải kiểm tra cho một cái gì đó trước khi gọi pthread_cond_wait(), nếu bạn không và nếu bạn là dấu hiệu chức năng đã được gọi là trước đó, sau đó bạn có thể ngủ mãi mãi.

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