2010-07-09 59 views
1
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <pthread.h> 
#include <semaphore.h> 
#define WORK_SIZE 1024 
pthread_mutex_t work_mutex; 
char work_area[WORK_SIZE]; 
void *thread_start(void *); 
int main() { 
pthread_t a_thread; 
pthread_mutex_init(&work_mutex,NULL); 
pthread_create(&a_thread,NULL,thread_start,NULL); 
while(1) 
{ 
pthread_mutex_lock(&work_mutex); 
printf("Enter some text\n"); 
fgets(work_area, WORK_SIZE, stdin); 
pthread_mutex_unlock(&work_mutex); 
} 
return 0; 
} 

void *thread_start(void *arg) 
{ 
sleep(1); 
while(1) 
{ 
pthread_mutex_lock(&work_mutex); 
printf("You enetered %d char",strlen(work_area)); 
pthread_mutex_unlock(&work_mutex); 
} 
} 

当我执行程序时,在释放主线程中的互斥锁之后,它每次在第二个线程获得锁之前都会再次获得锁。我期待一旦主线程释放锁,已经被阻塞的第二个线程将获得锁,并在主线之前开始执行。使用互斥锁实现生产者/消费者

更清楚,我得到这样的输出类型: -

Enter some text 
qwerty 
Enter some text 
asdaf 
Enter some text 
jkdf 
Enter some text 

回答

1

为了保证建议使用信号量,我认为你观察到的行为的原因如下。

  1. 直到调用thread_start中的pthread_mutex_lock,main中的循环才会被阻止。
  2. 因此,唯一的一次thread_start的循环将有机会打电话pthread_mutex_lock是当线程执行主要的时间片期满
  3. 该时间片到期发生的文化的机会,同时释放锁是微乎其微。这是因为主线程在等待ENTER键时从阻塞状态唤醒时可能会有新的时间片。

注意,这个解释假设一个核心。但即使在多核系统上,thread_start线程也只会在另一个线程的时间片耗尽时才被调度。在主线程不锁定的情况下发生这种情况的可能性很小。

我上述假设的一个可能的测试是在释放锁后调用pthread_yield。你可能会想在两个线程中做到这一点。即使那样我也不认为它会保证每次都有线程切换。

+0

pthread_yield就是我正在寻找的东西。 程序现在运行得非常好。 非常感谢。 – Ashish 2010-07-09 14:23:24

2

它只是似乎这样给你。在进行主数据输入之前,您将锁定的数据量要比输出该数据行的数量级要大。在那段时间内,另一个线程只会阻塞。你的主要将释放锁,稍后再获取它。

如果你这样做的时间够长 - 也许是数千次 - 你会看到它的工作。但是最好将主输入行复制到队列中,或者通过锁来保护其他一些内存。然后另一个线程将有机会得到它。

编辑:

一般的想法是这样的。我的代码添加很糟糕,但应该足够用于插图。

int main() 
{ 
    pthread_t a_thread; 
    pthread_mutex_init(&work_mutex, NULL); 
    pthread_create(&a_thread, NULL, thread_start, NULL); 

    memset(work_area, '\0', sizeof(work_area)); 


    char input[WORK_SIZE - 1]; 

    while (1) 
    { 
     printf("Enter some text\n"); 
     fgets(input, WORK_SIZE, stdin); 

     pthread_mutex_lock(&work_mutex); 
     strcpy(work_area, input); 
     pthread_mutex_unlock(&work_mutex); 
    } 

    return 0; 
} 

void *thread_start(void *arg) 
{ 
    sleep(1); 

    while (1) 
    { 
     pthread_mutex_lock(&work_mutex); 

     if (work_area[0] != '\0') 
     { 
      printf("You enetered %d char\n", strlen(work_area)); 
      work_area[0] = '\0'; 
     } 

     pthread_mutex_unlock(&work_mutex); 
    } 
} 
+0

您能否详细说明一下你的意思 - “但是,将main中的输入行复制到队列中会更好或一些其他的内存保护锁“ – Ashish 2010-07-09 13:49:50

+0

我想问的是: - 假设我们有线程A已经获得锁互斥和做一些工作。同时,线程B,C和D也尝试在同一个互斥锁上按该顺序锁定。他们将全部被阻止。现在,如果A重新开始锁定,然后又试图锁定哪一个锁定下一个锁定?是不是线程B应该得到锁,因为它是第一个试图获得锁的线程? – Ashish 2010-07-09 13:56:48

+0

您无法可靠地预测哪个线程将获得锁定,因此您处于操作系统的摆布之中。在你的代码中,你正在给打印线程一个微小的窗口(微秒),它可以获取锁。它最终会得到控制,但可能需要很长时间才能打到精确的窗口。 – Duck 2010-07-09 14:19:34

1

您可能希望创建和初始化一个信号,然后在第二个线程等待主功能时,输入被送到它预示着事件。

检查有条件的等待和信号量。

第二个线程不知道在主线程中生成了什么事件。 For your reference

+0

我同意信号灯是解决这个问题的更好方法。 但是不能通过使用Mutex来实现吗?问题不在于是否使用信号量或互斥量。我想知道为什么当主线程释放锁时,已经被阻塞的第二线程在主线程之前没有得到控制权。 – Ashish 2010-07-09 13:47:55

+0

我同意。信号量通常非常适合生产者消费者风格问题。它可以将同步和队列深度管理合并为一个抽象。 – torak 2010-07-09 13:48:08

0

在阅读torak给出的评论后,我改变了代码。现在它按预期正常工作。

[[email protected] threads]# diff -Nurp mutex.c mutex_modified.c 
--- mutex.c 2010-07-09 19:50:51.000000000 +0530 
+++ mutex_modified.c 2010-07-09 19:50:35.000000000 +0530 
@@ -18,6 +18,7 @@ pthread_mutex_lock(&work_mutex); 
printf("Enter some text\n"); 
fgets(work_area, WORK_SIZE, stdin); 
pthread_mutex_unlock(&work_mutex); 
+sleep(1); 
} 
return 0; 
} 
@@ -30,5 +31,6 @@ while(1) 
pthread_mutex_lock(&work_mutex); 
printf("You enetered %d char",strlen(work_area)); 
pthread_mutex_unlock(&work_mutex); 
+sleep(1); 
} 
} 

虽然它仍然很混乱。尽管这是一个测试程序,但在编写实际应用程序时应该如何避免这种情况呢?

+0

对于真正的应用程序:使用'pthread_yield'而不是'sleep(1)'。 – 2010-07-09 14:32:15

0

这里有一个稍微笨拙的解决方案,有保障的工作的:#include

#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <pthread.h> 
#include <semaphore.h> 

#define WORK_SIZE 1024 
pthread_mutex_t work_mutex; 
char input_area[WORK_SIZE]; 
char work_area[WORK_SIZE]; 

void *thread_start(void *); 

int main() 
{ 
    pthread_t a_thread; 
    pthread_mutex_init(&work_mutex,NULL); 
    work_area[0] = 0; 

    pthread_create(&a_thread,NULL,thread_start,NULL); 
    while(1) { 
     printf("Enter some text\n"); 
     fgets(input_area, WORK_SIZE, stdin); 
     pthread_mutex_lock(&work_mutex); 
     while (work_area[0] != 0) { 
      pthread_mutex_unlock(&work_mutex); 
      sleep(1); 
      pthread_mutex_lock(&work_mutex); 
     } 
     memcpy(work_area, input_area, WORK_SIZE); 
     pthread_mutex_unlock(&work_mutex); 
    } 
    return 0; 
} 

void *thread_start(void *arg) 
{ 
    while(1) 
    { 
     pthread_mutex_lock(&work_mutex); 
     if (work_area[0] > 0) { 
      printf("you enetered %d char\n",strlen(work_area)); 
      work_area[0] = 0; 
      pthread_mutex_unlock(&work_mutex); 
     } else { 
      pthread_mutex_unlock(&work_mutex); 
      sleep(1); 
     } 
    } 
} 

注意,因为POSIX互斥锁是线程专用的,消费者线程不能在信号等待从生产线,所以这里每秒醒来检查新数据。同样,如果生产者需要将某些东西放在队列中并且工作区已满,则会等待几秒钟,直到消费者开始清空缓冲区。您可以通过使用pthread_yield代替sleep摆脱一些延误,但那么你的线程将“忙等待”,消耗大量的CPU为自己的病情一次次检查要满足

请注意,如果你想让它要为0字符条目打印一行,您可以添加一个单独的布尔值来指示队列中是否有新数据。

0

@torax *“注意,这说明假定单核。但即使在多核系统的thread_start线程只会被安排在另一个线程的时间片用完” *

我认为这是不正确,主线程和thread_start可以在两个核心上并行调度