编写简单的C代码,尝试控制两个不同线程的输出:
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
sem_t sem;
void* thread_func(void* aArgs)
{
printf("Entering thread %p with %d\n", (void*)pthread_self(), (int)aArgs);
int i = 0;
for(;i < 10; i++)
{
sem_wait(&sem);
if ((i % 2) == (int)aArgs)
printf("val is %d in thread %p \n", i, (void*)pthread_self());
sem_post(&sem);
}
}
int main()
{
pthread_t thread_1, thread_2;
sem_init(&sem, 0, 1);
pthread_create(&thread_1, NULL, (void*)thread_func, (void*)0);
pthread_create(&thread_2, NULL, (void*)thread_func, (void*)1);
pthread_join(thread_1, NULL);
pthread_join(thread_2, NULL);
sem_destroy(&sem);
return 0;
}
我想要实现的是混合奇数和偶数的序列。但是我从一个线程接收所有数字,然后从第二个线程接收所有其他数字,就像这样(即使我增加了循环计数器的大小):
Entering thread 0xb75f2b40 with 0
val is 0 in thread 0xb75f2b40
val is 2 in thread 0xb75f2b40
val is 4 in thread 0xb75f2b40
val is 6 in thread 0xb75f2b40
val is 8 in thread 0xb75f2b40
Entering thread 0xb6df1b40 with 1
val is 1 in thread 0xb6df1b40
val is 3 in thread 0xb6df1b40
val is 5 in thread 0xb6df1b40
val is 7 in thread 0xb6df1b40
val is 9 in thread 0xb6df1b40
问题是为什么两个独立的线程表现得像两个连续的任务?为什么第二个线程没有执行控制直到第一个没有完成所有的东西?
我已经尝试将pthread_yield()添加到for循环的末尾,但情况并没有显着改变:有时我会得到预期的输出,有时 - 如上所述。
UPD。我如何实现确定性的逐个线程执行?是否有任何同步原语?
答案 0 :(得分:4)
如果要获得所需的输出,则应使用两个信号量而不是一个。每个线程应该等待自己的信号量,并在完成每个循环迭代后发布其他线程的信号量。主线程可以创建一个值为1的信号量,另一个值为零以启动正确的值。这将强制两个线程以交替顺序运行。
当前编写程序时,执行sem_post
后跟sem_wait
可能会导致同一个线程立即获取信号量(在单个cpu系统上)。我很惊讶pthread_yield
无效,但使用两个信号量无论如何都能保证正确的排序。
答案 1 :(得分:2)
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM 3
static sem_t sem[NUM];
static void *thread_func(void *args)
{
int i;
for (i = 0; i < 10; ++i) {
int cur = (long)args; /* current thread number */
int next = (cur + 1) % NUM; /* next thread number*/
if ((i % NUM) != cur)
continue;
sem_wait(&sem[cur]); /* lock this thread's semaphore */
printf("val is %d, thread num = %ld\n", i, (long)args);
sem_post(&sem[next]); /* unlock next thread's semaphore */
}
return NULL;
}
int main(void)
{
size_t i;
pthread_t t[NUM];
for (i = 0; i < NUM; ++i)
sem_init(&sem[i], 0, 0); /* locked */
for (i = 0; i < NUM; ++i)
pthread_create(&t[i], NULL, thread_func, (void *)i);
sem_post(&sem[0]);
for (i = 0; i < NUM; ++i)
pthread_join(t[i], NULL);
for (i = 0; i < NUM; ++i)
sem_destroy(&sem[i]);
return 0;
}
输出:
val is 0, thread num = 0
val is 1, thread num = 1
val is 2, thread num = 2
val is 3, thread num = 0
val is 4, thread num = 1
val is 5, thread num = 2
val is 6, thread num = 0
val is 7, thread num = 1
val is 8, thread num = 2
val is 9, thread num = 0
答案 2 :(得分:1)
您继续在循环的相同迭代中调用sem_wait
和sem_post
,因此线程在其时间片的持续时间内保持对信号量的控制 - 如调用sem_post
后,在下一次迭代中会立即再次调用sem_wait
(在同一个线程中)。
以下是使用条件变量解决问题的方法:
pthread_mutex_t mut;
pthread_cond_t print_cond;
int print_thread; //equals 0 or 1
这些是用于在两个线程之间同步输出的全局变量。当我们想要打印第一个线程时,print_thread
等于0,当我们想要打印第二个线程时,thread_func
等于1。
在for(;i < 10; i++)
{
pthread_mutex_lock(&mut);
if ((i % 2) == (int)aArgs){
while (print_thread != (int)aArgs){
pthread_cond_wait(&print_cond, &mut);
}
printf("val is %d in thread %p \n", i, (void*)pthread_self());
print_thread = 1 - (int)aArgs;
pthread_cond_signal(&print_cond);
pthread_mutex_unlock(&mut);
} else {
pthread_mutex_unlock(&mut);
}
}
内:
Entering thread 0xb6fbcb70 with 1
Entering thread 0xb77bdb70 with 0
val is 0 in thread 0xb77bdb70
val is 1 in thread 0xb6fbcb70
val is 2 in thread 0xb77bdb70
val is 3 in thread 0xb6fbcb70
val is 4 in thread 0xb77bdb70
val is 5 in thread 0xb6fbcb70
val is 6 in thread 0xb77bdb70
val is 7 in thread 0xb6fbcb70
val is 8 in thread 0xb77bdb70
val is 9 in thread 0xb6fbcb70
使用此代码,您应获得类似于以下内容的输出:
print_thread
请注意,此解决方案可以很好地扩展到只有两个线程的打印:唯一必要的更改是正确更新{{1}}。
答案 3 :(得分:0)
pthread_yield()
是非标准调用,请改用sched.h中的sched_yield()
。
此外,我会在0处初始化信号量,并在创建两个线程后调用sem_post
。
所以线程的代码看起来像
for(;i < 10; i++)
{
sem_wait(&sem);
if ((i % 2) == (int)aArgs)
printf("val is %d in thread %p \n", i, (void*)pthread_self());
sem_post(&sem);
sched_yield();
}
主要是:
sem_init(&sem, 0, 0);
pthread_create(&thread_1, NULL, (void*)thread_func, (void*)0);
pthread_create(&thread_2, NULL, (void*)thread_func, (void*)1);
sem_post(&sem);
得到的是:
Entering thread 0x7f74c7697700 with 0
val is 0 in thread 0x7f74c7697700
Entering thread 0x7f74c6e96700 with 1
val is 2 in thread 0x7f74c7697700
val is 4 in thread 0x7f74c7697700
val is 1 in thread 0x7f74c6e96700
val is 3 in thread 0x7f74c6e96700
val is 5 in thread 0x7f74c6e96700
val is 7 in thread 0x7f74c6e96700
val is 6 in thread 0x7f74c7697700
val is 9 in thread 0x7f74c6e96700
val is 8 in thread 0x7f74c7697700