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

时间:2018-11-19 11:27:32

标签: c pthreads mutex

我正在尝试使用共享变量和互斥锁来同步三个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 个答案:

答案 0 :(得分:0)

当多个线程尝试同时获取互斥锁时,它们中的任何一个都可以获取它。因此,如果“错误”线程获取了互斥锁,则必须以某种方式屈服,以便正确的线程获取互斥锁。在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

将多个变量传递给线程函数的方式与传递字符非常相似。您可以使用结构来代替char。例如:

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';

,并说他们的地址为&(w[0])(或等价地为&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。仅使用三个线程就不是一个真正的问题,但是我认为在这里介绍这个概念可能是一个好主意。仅当发现当前线程不合适时,我们才唤醒所有等待条件变量的线程。

如果只用信号通知条件变量,则可能有两个错误的线程会交替出现;这就是我们真正需要广播的原因。