2016-03-21 75 views
0

此程序的目标是复制用户输入单词的字符串,并使用多线程处理单词。每个线程复制每四个单词,例如第一个线程复制第一个和第五个单词,第二个复制第二个和第六个单词等等。我对互斥体做了相当多的研究,我相信我已经正确实施了互斥锁然而,当它打印时,字符串仍然显得杂乱无章。有人可以阐明为什么线程不同步吗?使用互斥锁的Pthread同步不能正确同步单词

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

void *processString(void *); 

char msg1[100]; 
char msg2[100]; 
char * reg; 
char * token; 
char * tokens[10]; 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t = PTHREAD_COND_INITIALIZER; 

int main(){ 
    int i = 0, j; 
    pthread_t workers[4]; 

    printf("Please input a string of words separated by whitespace characters: \n"); 
    scanf("%99[^\n]", msg1); //take in a full string including whitespace characters 

    //tokenize string into individual words 
    token = strtok(msg1, " "); 
    while(token != NULL){ 
     tokens[i] = (char *) malloc (sizeof(token)); 
     tokens[i] = token; 
     token = strtok(NULL, " "); 
     i++; 
    } 

    for(j = 0; j < 4; j++){ 
     if(pthread_create(&workers[j], NULL, processString, (void *) j)) 
      printf("Error creating pthreads"); 
    } 

    for(i = 0; i < 4; i++){ 
     pthread_join(workers[i], NULL); 
    } 
    pthread_mutex_destroy(&lock); 

    printf("%s\n", msg2); 

    return 0; 
} 

//each thread copies every fourth word 
void *processString(void *ptr){ 
    int j = (int) ptr, i = 0; 

    pthread_mutex_lock(&lock); 

    while(tokens[i * 4 + j] != NULL){ 
     reg = (char *) malloc (sizeof(tokens[i * 4 + j])); 
     reg = tokens[i * 4 + j]; 
     strcat(msg2, reg); 
     strcat(msg2, " "); 
     i++; 
    } 

    pthread_mutex_unlock(&lock); 
    return NULL; 
} 
+1

如果你想控制线程的运行顺序,你需要类似条件变量或信号量的东西。互斥体只确保相互排斥,而不是排序。 – EOF

+1

什么EOF说。但是,你的代码也会泄漏内存。在其循环的每次迭代中,函数'processString()'分配一块内存,将指向该块的指针赋值给变量'reg',然后立即用另一个指针覆盖该指针,从而失去指向分配的唯一指针块。然而,从某种意义上说,这也是一样,因为你为一个*指针*分配了足够的空间,而似乎(误导)的意图是为指向字符串分配足够的空间。 –

+1

另外,您的锁定是错误的。一旦每个线程获得互斥锁,它将在释放它之前执行所有*工作。互斥对于防止结果字符串被破坏是有效的,从这个意义上说,您正确使用它。但是,只有当输入中少于五个单词时才有可能获得所需的输出。 –

回答

1

由于@EOF在评论中写道,互斥提供只有互斥。它们阻止多个协作线程同时运行,但它们并不固有地控制它们被这些线程获取的顺序。另外,正如我在自己的评论中所描述的那样,互斥锁提供互斥功能:如果一个线程持有互斥锁,则其他线程将无法获取该互斥锁,也不会继续尝试这样做,直到该互斥锁被释放。

没有本地同步对象,它直接提供让线程轮流使用。毕竟,这通常不是你想要的。你可以用信号量来安排它,但是当你添加更多的线程时,它会很快变得混乱。一个非常干净的解决方案涉及使用共享的全局变量来指示要运行哪个线程。对变量的访问必须由互斥体保护,因为所涉及的所有线程都必须读取和写入它,但是存在一个问题:如果当前拥有该互斥体的线程不是要轮到它运行的线程?

所有线程都有可能循环,持续获取互斥锁,测试变量以及继续或释放互斥锁。不幸的是,如此繁忙的等待往往表现得非常糟糕,并且一般来说,您无法确信在执行的任何给定点上可以取得进展的线程都能在有限的时间内获得互斥。

这是condition variables进来的地方。条件变量是一个同步对象,它允许任意数量的线程挂起活动,直到满足某些条件为止,如另一个未挂起的线程所判断。使用这样的工具可以避免性能枯竭的忙碌等待,在您的情况下,它可以帮助确保您的所有线程都有机会在有限的时间内运行。条件变量通用每线程使用模型如下:

  1. 获得互斥保护,通过该判断是否能继续进行
  2. 试验I是否可以进行共享变量(多个)。如果是,请跳至步骤5.
  3. 我现在无法进行。对条件变量执行等待。
  4. 我已经从等待中惊醒;回到步骤2.
  5. 做我需要做的工作。
  6. 广播一个信号唤醒等待条件变量的所有线程。
  7. 释放互斥锁。

虽然可能存在差异,但我建议您不要因此而有所不同,除非您确切知道您为什么要这么做,以及您确切知道为什么您想要的变化是安全的。还要注意,当一个线程在与一个给定的互斥体相关联的条件变量上执行一个等待时,它会在等待时自动释放该互斥体,并在等待返回之前重新获取该互斥体。这允许其他线程同时进行,特别是等待相同的条件变量。

因为它适用于你的问题,所以你希望你的线程测试的共享状态是前面提到的变量,它指示了它是哪个线程,你希望你的线程等待的条件是它已经变得不同了线程的转变(但这是隐含的,你使用条件变量的方式;条件变量本身是通用的)。还要注意,这意味着每个线程在发出其他线程信号之前必须完成的部分工作是更新线程的状态。而且由于每个线程可能需要多次轮换,您将需要将整个程序包装在一个循环中。

+0

谢谢!那正是我需要的。我的程序正在做我所需要的。 :) – Turbotec