在 C 中使用共享变量和互斥锁进行线程同步
Thread synchronizing using shared variables and mutex in C
我正在尝试使用共享变量和互斥锁来同步三个 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;
}
当多个线程同时尝试获取互斥量时,其中任何一个都可以获得它。因此,如果 "wrong" 线程获取互斥锁,它必须以某种方式让出,以便正确的线程获取互斥锁。在 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()
wakes up one thread already waiting on the condition variable, and pthread_cond_broadcast()
唤醒所有已经在等待条件变量的线程。
在正常情况下,我们只唤醒一个线程,尽量避免所谓的thundering herd problem。只有三个线程并不是真正的问题,但我认为在这里引入这个概念可能是个好主意。只有当我们发现当前线程不是正确的线程时,我们才会唤醒所有等待条件变量的线程。
如果我们只在条件变量上发出信号,那么两个错误的线程可能会交替出现;这就是我们真正需要广播的原因。
我正在尝试使用共享变量和互斥锁来同步三个 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;
}
当多个线程同时尝试获取互斥量时,其中任何一个都可以获得它。因此,如果 "wrong" 线程获取互斥锁,它必须以某种方式让出,以便正确的线程获取互斥锁。在 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()
wakes up one thread already waiting on the condition variable, and pthread_cond_broadcast()
唤醒所有已经在等待条件变量的线程。
在正常情况下,我们只唤醒一个线程,尽量避免所谓的thundering herd problem。只有三个线程并不是真正的问题,但我认为在这里引入这个概念可能是个好主意。只有当我们发现当前线程不是正确的线程时,我们才会唤醒所有等待条件变量的线程。
如果我们只在条件变量上发出信号,那么两个错误的线程可能会交替出现;这就是我们真正需要广播的原因。