了解并行线程执行

时间:2015-02-22 22:15:53

标签: c multithreading pthreads

编写简单的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。我如何实现确定性的逐个线程执行?是否有任何同步原语?

4 个答案:

答案 0 :(得分:4)

如果要获得所需的输出,则应使用两个信号量而不是一个。每个线程应该等待自己的信号量,并在完成每个循环迭代后发布其他线程的信号量。主线程可以创建一个值为1的信号量,另一个值为零以启动正确的值。这将强制两个线程以交替顺序运行。

当前编写程序时,执行sem_post后跟sem_wait可能会导致同一个线程立即获取信号量(在单个cpu系统上)。我很惊讶pthread_yield无效,但使用两个信号量无论如何都能保证正确的排序。

答案 1 :(得分:2)

只想为任意数量的线程展示answer JS1的代码:

#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_waitsem_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