在 C 中使用共享变量和互斥锁进行线程同步

Posted

技术标签:

【中文标题】在 C 中使用共享变量和互斥锁进行线程同步【英文标题】:Thread synchronizing using shared variables and mutex in C 【发布时间】:2018-11-19 11:27:31 【问题描述】:

我正在尝试使用共享变量和互斥锁同步三个 pthread,以便它们创建输出:123123123... 但是,我能想到的只是使用 while 循环,如下面的代码所示。 是否可以让代码更优雅,而不让线程休眠和使用while循环?

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

static pthread_mutex_t cs_mutex;
char p;
int q;

void* print(void *pParam)

    char c = *(char*)pParam;
    int i;

    for (i = 0; i < 100; i++)
    
        while(p!=c) sleep(0.2);
        pthread_mutex_lock(&cs_mutex);
        printf("%c", c);
        fflush(stdout);
        q=(q+1)%4;
        if(q==0)q=1;
        p=q+48;
        pthread_mutex_unlock(&cs_mutex);
    

    return 0;


int main(void)

    pthread_t hPrint1;
    pthread_t hPrint2;
    pthread_t hPrint3;

    pthread_mutex_init(&cs_mutex, NULL);

    char c1 = '1';
    char c2 = '2';
    char c3 = '3';

    p=c1;
    q=1;

    pthread_create(&hPrint1, NULL, print, (void*)&c1);
    pthread_create(&hPrint2, NULL, print, (void*)&c2);
    pthread_create(&hPrint3, NULL, print, (void*)&c3);

    getchar();

    pthread_mutex_destroy(&cs_mutex);

    return 0;

【问题讨论】:

您可能正在寻找条件等待。 【参考方案1】:

当多个线程同时尝试获取互斥体时,其中任何一个都可以获取它。因此,如果“错误”线程获取了互斥锁,它必须以某种方式让步,以便正确的线程获取互斥锁。在 OP 的代码中,sleep(0.2) 尝试执行此操作。 (这是一个忙碌的等待,并不能按预期工作,因为 unistd.h sleep() 将整数秒数作为参数。)

更好的选择是使用互斥体、条件变量和序列索引作为共享变量。在伪代码中,每个线程都会这样做:

Function Thread(mynumber, mychar):

    Lock mutex

    Loop:
        Wait on condition variable
        If index >= limit:
            Signal on condition variable
            Unlock mutex
            Return
        Else
        If (index % mynumber == 0):
            Output mychar
            Signal on condition variable
        Else:
            Broadcast on condition variable
        End If
    End Loop
End Function

将多个变量传递给线程函数的方式与传递字符的方式非常相似。而不是一个字符,你只使用一个结构。例如:

struct work 
    int  mynumber;  /* Thread number: 0, 1, 2 */
    int  mychar;    /* Character to output: '1', '2', '3' */
;

您可以将struct work w[3]; 声明为全局变量或在您的main() 中,并使用例如初始化它

    struct work w[3];
    w[0].mynumber = 0;  w[0].mychar = '1';
    w[1].mynumber = 1;  w[1].mychar = '2';
    w[2].mynumber = 2;  w[2].mychar = '3';

并将他们的地址称为&amp;(w[0])(或等同于&amp;w[0])。

在线程函数中,可以使用例如

void *worker(void *payload)

    struct work *const w = payload;

    /* w->mynumber  is the number (0, 1, 2) of this thread,
       w->mychar    is the char ('1', '2', '3') to output */

注意pthread_cond_signal() 唤醒一个已经在条件变量上等待的线程,pthread_cond_broadcast() 唤醒所有已经在条件变量上等待的线程。

在正常情况下,我们只唤醒一个线程,尽量避免所谓的thundering herd problem。仅仅三个线程并不是一个真正的问题,但我认为在这里引入这个概念可能是一个好主意。只有当我们发现当前线程不是正确的线程时,我们才会唤醒所有等待条件变量的线程。

如果我们只在条件变量上发出信号,那么两个错误的线程可能会交替出现;这就是我们真正需要广播的原因。

【讨论】:

以上是关于在 C 中使用共享变量和互斥锁进行线程同步的主要内容,如果未能解决你的问题,请参考以下文章

UNIX网络编程:互斥锁和条件变量

Linux下的互斥锁和条件变量

Linux多线程同步之互斥量和条件变量

Go36-27,28-条件变量

C#学习笔记---线程同步:互斥量信号量读写锁条件变量

条件变量和互斥锁的使用