使用互斥锁进行Pthread同步不能正确同步单词

时间:2016-03-21 18:29:10

标签: c multithreading synchronization pthreads

该程序的目标是使用多线程复制用户输入逐字输入的字符串。每个线程复制每四个字,所以例如第一个线程复制第一个和第五个字,第二个复制第二个和第六个字等等。我对互斥锁进行了相当多的研究,我相信我已经正确地实现了互斥锁然而,当打印时,字符串仍然是混乱的胡说八道。有人可以解释为什么线程不同步吗?

#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 个答案:

答案 0 :(得分:1)

正如@EOF在评论中写道,互斥锁提供互斥。它们防止多个协作线程同时运行,但它们本身并不提供对这些线程获取它们的顺序的任何控制。另外,正如我在评论中所描述的那样,互斥锁提供互斥:如果一个线程持有互斥锁,那么没有其他线程可以获取该互斥锁,也不会继续尝试这样做,直到那个互斥锁被释放了。

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

所有线程都可以循环,连续获取互斥锁,测试变量,以及继续或释放互斥锁。不幸的是,这种忙碌的等待往往表现得非常糟糕,而且一般来说,你无法确信在执行过程中任何给定点可以取得进展的线程将设法在有限时间内获取互斥锁。

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

  1. 获取保护共享变量的互斥锁,以判断我是否可以继续
  2. 测试我是否可以继续。如果是,请跳至步骤5.
  3. 我现在不能继续。在条件变量上执行等待。
  4. 我从等待中醒来;回到第2步。
  5. 做我需要做的工作。
  6. 广播信号以唤醒等待条件变量的所有线程。
  7. 释放互斥锁。
  8. 这方面的变化是可能的,但我建议你不要改变它,除非你确切知道为什么要这样做,并确切地说为什么你想到的变化是安全的。另请注意,当线程对与给定互斥锁关联的条件变量执行等待时,它会在等待时自动释放该互斥锁,并在从等待返回之前重新获取该互斥锁。这允许其他线程在此期间继续进行,特别是等待相同的条件变量。

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