Linux多线程-线程同步

线程同步

当多个线程同时对一个共享数据进行操作时,会导致数据竞争,下面例子展示了数据竞争的情况:

 1 #include <pthread.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <unistd.h>
 6 
 7 static int val = 0;
 8 void* threadEntry(void* arg)
 9 {
10     for(int i = 0; i<1000000; ++i)
11     {
12         val = val + 1;
13     }
14     return NULL;
15 }
16 
17 int main(int argc, char** argv)
18 {
19     pthread_t th1,th2;
20     pthread_create(&th1, NULL, threadEntry, NULL);
21     pthread_create(&th2, NULL, threadEntry, NULL);
22     pthread_join(th1, NULL);
23     pthread_join(th2, NULL);
24     printf("calc result : %d\n", val);
25     return 0;
26 }

输出:

calc result : 1013285

两个线程分别对共享资源进行自增操作,并没有得到预期结果,这是由于竞争导致的。为了确保对数据操作的完整性,需要引入同步机制。

互斥锁

在Linux中,互斥锁用 pthread_mutex_t 结构体描述。该结构体定义大致如下:

typedef struct
{
    // 互斥量的类型属性
    // PTHREAD_MUTEX_NORMAL - 正常互斥量
    // PTHREAD_MUTEX_ERRORCHECK - 错误检查互斥量
    // PTHREAD_MUTEX_RECURSIVE - 递归互斥量
    // PTHREAD_MUTEX_DEFAULT - 默认互斥量类型
    int type;
    // 保留字段
    long int __reserved;
    // 锁的拥有者线程ID
    __pthread_mutex_slist_t *owner;
    // 等待该互斥量的线程列表
    __pthread_mutex_slist_t __list;
} pthread_mutex_t;

初始化互斥锁-pthread_mutex_init() 函数

pthread_mutex_init() 函数用于初始化一个互斥量,在使用互斥量之前,必须先初始化它。该函数定义如下:

#include <pthread.h>

 int pthread_mutex_init(pthread_mutex_t *mutex, 
                        const pthread_mutexattr_t* attr);

参数说明

  • mutex:指向要初始化的互斥量的指针。
  • attr:指向 pthread_mutexattr_t 类型的指针,用于描述互斥量属性。

返回值

  • 如果初始化成功,返回值为 0。
  • 如果初始化失败,返回错误代码。有如下常见错误:
    • EINVAL:attr 参数所描述的属性值无效。
    • ENOMEM:没有足够的系统资源来创建互斥量。
    • EBUSY:另一个线程之前已经锁住了该互斥量,而且该互斥量不是递归型的。

系统提供了宏 PTHREAD_MUTEX_INITIALIZER,通过该宏也可以初始化一把锁:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

销毁互斥量-pthread_mutex_destroy() 函数

pthread_mutex_destroy() 函数用于销毁一个已经初始化的互斥量。当互斥量不再需要时,应该使用该函数进行销毁,以释放相关的资源。该函数定义如下:

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数说明

  • mutex:指向要销毁的互斥量结构体的指针。

返回值

  • 如果初始化成功,返回值为 0。
  • 如果初始化失败,返回错误代码。有如下常见错误:
    • EINVAL:互斥量未被正确初始化。
    • EBUSY:互斥量已被锁住,无法销毁。

需要注意的是,在销毁互斥量之前,需要保证没有其他线程正在使用该互斥量。

锁操作

对互斥的操作分为加锁和解锁。

加锁-pthread_mutex_lock() 函数

pthread_mutex_lock() 函数用于获取互斥锁,如果当前锁已被其他线程获取,则调用线程会被阻塞,直到锁被释放为止。该函数定义如下:

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

参数说明

  • mutex:指向要加锁的互斥量结构体的指针。

返回值

  • 如果函数成功地对互斥量加锁,则返回值为 0。
  • 如果出现错误,返回相应的错误代码。

一旦获取了锁,线程就可以访问受保护的资源。在完成对共享资源的访问后,线程应该调用 pthread_mutex_unlock() 函数来释放锁。否则将造成死锁。

尝试加锁-pthread_mutex_trylock() 函数

pthread_mutex_trylock() 函数的功能是尝试获取锁,该函数并不是一个阻塞函数,不管是否获取锁,都会立刻返回。该函数定义如下:

#include <pthread.h>

int pthread_mutex_trylock(pthread_mutex_t *mutex);

参数说明

  • mutex:指向要加锁的互斥量结构体的指针。

返回值

  • 如果函数成功地对互斥量加锁,则返回值为 0。
  • 如果出现错误,返回相应的错误代码。常见错误代码如下:
    • EBUSY:互斥锁已经被其他线程占用。
    • EINVAL:互斥锁未初始化,或者是无效的互斥锁。

如果互斥锁当前没有被其他线程持有,那么调用线程会成功获取锁。如果互斥锁已经被其他线程持有,那么函数会立即返回并设置错误码为 EBUSY,不会阻塞线程。

解锁-pthread_mutex_unlock() 函数

pthread_mutex_unlock() 函数用于释放互斥锁,该函数定义如下:

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

参数说明

  • mutex:指向要解锁的互斥量结构体的指针。

返回值

  • 如果成功释放互斥锁,则返回值为 0。
  • 如果出现错误,返回相应的错误代码。

互斥锁属性

互斥锁的属性用 pthread_mutexattr_t 结构体描述。它是一个不透明的结构体,其具体定义通常是由系统实现。开发者无需了解结构体的具体定义,无需直接操作该结构体的成员,而是使用相应的函数来进行操作。

初始化互斥锁属性

pthread_mutexattr_init() 函数用于初始化 pthread_mutexattr_t 结构体。此函数将为互斥锁属性对象分配内存,并将其初始化为一个有效的、可用的状态。该函数定义如下:

#include <pthread.h>

int pthread_mutexattr_init(pthread_mutexattr_t *attr);

参数说明

  • attr:指向 pthread_mutexattr_t 结构体的指针,用于接收初始化后的互斥锁属性对象。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

销毁互斥锁属性

pthread_mutexattr_destroy() 函数用于销毁已经初始化的 pthread_mutexattr_t 结构体,释放相关资源。该函数定义如下:

#include <pthread.h>

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);

参数说明

  • attr:指向已初始化的 pthread_mutexattr_t 结构体的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

设置互斥锁的类型

pthread_mutexattr_settype() 函数用于设置互斥锁的类型,该函数定义如下:

#include <pthread.h>

int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);

参数说明

  • attr:指向已初始化的 pthread_mutexattr_t 结构体的指针。
  • type:要设置的互斥锁类型。常见的互斥锁类型有以下几种:
    • PTHREAD_MUTEX_NORMAL:默认类型,不具备死锁检测与恢复的特性。
    • PTHREAD_MUTEX_ERRORCHECK:具备错误检查的特性,在同一线程中重复加锁将返回一个错误。
    • PTHREAD_MUTEX_RECURSIVE:递归锁类型,允许同一线程多次加锁而不会产生死锁。
    • PTHREAD_MUTEX_DEFAULT:默认类型,与PTHREAD_MUTEX_NORMAL 类型相同。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

获取互斥锁类型

pthread_mutexattr_gettype() 函数用于获取互斥锁的类型,该函数定义如下:

#include <pthread.h>

int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);

参数说明

  • attr:指向已初始化的 pthread_mutexattr_t 结构体的指针。
  • type:指向整型变量的指针,用于存储获取到的互斥锁类型。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

设置互斥锁的共享属性

pthread_mutexattr_setpshared() 函数用于设置互斥锁的共享属性,指定互斥锁是否可以在进程间共享。该函数定义如下:

#include <pthread.h>

int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);

参数说明

  • attr:指向已初始化的 pthread_mutexattr_t 结构体的指针。
  • pshared:要设置的共享属性。常见的共享属性有以下两种:
    • PTHREAD_PROCESS_PRIVATE:互斥锁仅限于进程内部共享,不能在不同进程间共享。
    • PTHREAD_PROCESS_SHARED:互斥锁可以在不同进程间共享。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

获取互斥锁的共享属性

pthread_mutexattr_getpshared() 函数用于获取互斥锁的共享属性。该函数定义如下:

#include <pthread.h>

int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, 
                                 int *pshared);

参数说明

  • attr:指向已初始化的 pthread_mutexattr_t 结构体的指针。
  • pshared:指向整型变量的指针,用于存储获取到的共享属性。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

示例-递归锁

 1 #include <pthread.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <unistd.h>
 6 
 7 static pthread_mutex_t mutex;
 8 
 9 void func()
10 {
11     static int i = 0;
12     if(i >= 10) return;
13     pthread_mutex_lock(&mutex);
14     printf("func : %d\n", i++);
15     func();
16     pthread_mutex_unlock(&mutex);
17 }
18 
19 void* threadEntry(void* arg)
20 {
21     func(); 
22     return NULL;
23 }
24 
25 int main(int argc, char** argv)
26 {
27     pthread_t th;
28     pthread_mutexattr_t muattr;
29     pthread_mutexattr_init(&muattr);
30     pthread_mutexattr_settype(&muattr, PTHREAD_MUTEX_RECURSIVE);
31     pthread_mutex_init(&mutex, &muattr);
32 
33     pthread_create(&th, NULL, threadEntry, NULL);
34     pthread_join(th, NULL);
35 
36     pthread_mutexattr_destroy(&muattr);
37     pthread_mutex_destroy(&mutex);
38     return 0;
39 }

输出:

func : 0
func : 1
...
func : 8
func : 9

读写锁

对共享资源读操作是不会造成竞争的,真正导致竞争的原因是在写入时,其他线程获取该值,并用此值进行运行后重新写回。读写锁允许多个线程同时读取共享数据,但只允许一个线程写入共享数据,并且在写入时,其他任何线程都不允许读取。

读写锁用 pthread_rwlock_t 结构体描述,它是一个不透明的结构体,其具体定义通常是由系统实现。

初始化读写锁-pthread_rwlock_init() 函数

pthread_rwlock_init() 函数用于初始化读写锁,并指定锁的属性。该函数定义如下:

#include <pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *rwlock, 
                        const pthread_rwlockattr_t *attr);

参数说明

  • rwlock:指向要初始化的读写锁的指针。
  • attr:指向读写锁属性的指针。可以为NULL,表示使用默认属性。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

系统提供了宏 PTHREAD_RWLOCK_INITIALIZER,它可以用来快速初始化一个默认的读写锁。代码如下:

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

销毁读写锁-pthread_rwlock_destroy() 函数

pthread_rwlock_destroy() 函数用于销毁一个已经初始化的读写锁,并释放相关的资源。该函数定义如下:

#include <pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向要销毁的读写锁的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

读模式加锁-pthread_rwlock_rdlock() 函数

pthread_rwlock_rdlock() 函数用于以读模式锁定读写锁。读写锁允许多个线程同时以读模式访问共享资源,但是当有线程以写模式访问时,读写锁会阻塞所有试图以读或写模式访问共享资源的线程,直到写操作完成。该函数定义如下:

#include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向要锁定的读写锁的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

读模式尝试加锁-pthread_rwlock_tryrdlock() 函数

pthread_rwlock_tryrdlock() 函数用于以读模式锁定读写锁。如果无法获取读锁,它会立即返回而不是阻塞线程等待锁的释放。该函数定义如下:

#include <pthread.h>

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向要尝试锁定的读写锁的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

若读锁已被其他线程占用,则无法获取,函数将返回 EBUSY,表示资源忙碌。

写模式加锁-pthread_rwlock_wrlock() 函数

pthread_rwlock_wrlock() 函数用于以写模式锁定读写锁。如果已经有其他线程持有读锁,写锁的获取操作将会阻塞,直到所有的读锁都被释放。如果一个线程以写模式锁定了读写锁,其他任何线程(无论是读模式还是写模式)都无法同时锁定该读写锁,直到写操作完成。该函数定义如下:

#include <pthread.h>

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向要锁定的读写锁的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

写模式尝试加锁-pthread_rwlock_trywrlock() 函数

pthread_rwlock_trywrlock() 函数用于以写模式尝试锁定读写锁。该函数是非阻塞的,如果无法获取写锁,它会立即返回而不是阻塞线程等待锁的释放。该函数定义如下: 

#include <pthread.h>

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向尝试要锁定的读写锁的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

若写锁已被其他线程占用,则无法获取,函数将返回 EBUSY,表示资源忙碌。

解锁-pthread_rwlock_unlock() 函数

#include <pthread.h>

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向要解锁的读写锁的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

读写锁属性

读写锁属性通过 pthread_rwlockattr_t 结构体指定,该结构体是一个非透明结构体,其具体定义通常是由系统实现。开发人员通过系统提供的API进行操作。

#include <pthread.h>

//初始化
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);

//销毁
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);

共享属性:

#include <pthread.h>

//获取共享属性
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, 
                                  int *pshared);
     
//设置共享属性                             
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, 
                                  int pshared);

pshared参数描述了读写锁的共享属性,取值如下:

  • PTHREAD_PROCESS_PRIVATE:读写锁仅在创建它的进程中可用。
  • PTHREAD_PROCESS_SHARED:读写锁在多个进程之间共享。

偏好类型:

#include <pthread.h>

//设置读写偏好
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, 
                                  int pref);

//获取读写偏好                              
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, 
int *pref);

pref参数有如下取值:

  • PTHREAD_RWLOCK_PREFER_READER_NP:偏好于读者(默认选项)。当读写锁处于读模式时,如果有新的读者请求,系统更倾向于将锁分配给新的读者,以提高并发读取的效率。这意味着在有多个读者和一个写者竞争锁的情况下,系统更可能将锁分配给新的读者,而暂时不会将锁分配给写者。
  • PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:偏好于非递归写者。当读写锁处于写模式时,如果有新的写者请求,系统更倾向于将锁分配给新的写者,而不是之前已经获取写锁的线程。这样可以避免递归写锁的情况,即同一个线程在拥有写锁的情况下再次尝试获取写锁,从而避免死锁和不必要的复杂性。

条件变量

条件变量是一种同步机制,用于线程之间的协调和通信。条件变量通常与互斥锁结合使用,以实现复杂的线程同步。

条件变量被用来阻塞一个线程,当条件不满足时,线程通常先解开互斥锁进入阻塞状态,等待条件发生变化。一旦某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。如果此时满足条件,该线程继续向下执行。

条件变量用 pthread_cond_t 结构体描述,它是一个不透明的结构体,其具体定义通常是由系统实现。

初始化条件变量-pthread_cond_init() 函数

pthread_cond_init()函数用于初始化条件变量,该函数定义如下:

#include <pthread.h>

int pthread_cond_init(pthread_cond_t * cond, 
                      const pthread_condattr_t* attr);

参数说明

  • cond:指向要初始化的条件变量的指针。
  • attr:指向用于初始化条件变量属性对象的指针,如果传入NULL,则使用默认属性值。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

系统提供了 PTHREAD_COND_INITIALIZER 宏用于快速初始化条件变量,代码如下:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

销毁条件变量-pthread_cond_destroy() 函数

pthread_cond_destroy() 函数用于销毁条件变量,并释放相关的资源。该函数定义如下:

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

参数说明

  • cond:指向要销毁的条件变量的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

等待条件变量-pthread_cond_wait() 函数

pthread_cond_wait() 函数用于等待条件变量满足条件。在等待条件变量的过程中,当前线程会释放互斥量的所有权并阻塞,直到另一个线程唤醒它。该函数定义如下:

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

参数说明

  • cond:指向要等待的条件变量的指针。
  • mutex:指向与条件变量关联的互斥量的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

注意事项

  • 在调用 pthread_cond_wait() 之前,应该通过调用 pthread_mutex_lock() 获取互斥量的所有权,并在等待结束后再通过调用 pthread_mutex_unlock() 释放互斥量。
  • 被唤醒后,pthread_cond_wait() 函数会重新获取互斥量的所有权,并继续执行。
  • 在调用 pthread_cond_wait() 之前,通常会检查条件是否满足,以避免虚假唤醒(Spurious Wakeup)。通常通过 while 循环来实现,直到条件满足才会退出循环。

超时等待条件变量-pthread_cond_timedwait() 函数

pthread_cond_timedwait() 函数允许线程在指定时间内等待条件变量的满足,如果条件满足则立即返回,否则在超时之后返回。该函数定义如下:

#include <pthread.h>
#include <time.h>

struct timespec {
    time_t tv_sec;      // 秒数
    long   tv_nsec;     // 纳秒数
};

int pthread_cond_timedwait(pthread_cond_t *restrict cond, 
                           pthread_mutex_t *restrict mutex, 
                           const struct timespec *restrict abstime); 

参数说明

  • cond:指向要等待的条件变量的指针。
  • mutex:指向与条件变量关联的互斥量的指针。
  • abstime:指定等待超时的时间,用 struct timespec 结构体表示。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。常用错误值如下:
    • ETIMEDOUT:超时。

注意事项

  • 当线程被唤醒,或者等待超时时,函数会将关联的互斥量重新加锁,并返回。
  • 确保 abstime 参数指定的时间是未来的时间。

用法

 1 void *threadEntry(void *arg)
 2 {
 3     struct timespec timeout;
 4     clock_gettime(CLOCK_REALTIME, &timeout);
 5     timeout.tv_sec += 3; // 延时3s
 6     pthread_mutex_lock(&mutex);
 7     int ret = pthread_cond_timedwait(&cond, &mutex, &timeout);
 8     if (ret == 0)
 9     {
10         // 满足条件
11     }
12     else if (ret == ETIMEDOUT)
13     {
14         // 超时
15     }
16     else
17     {
18         // 其他错误
19     }
20     return NULL;
21 }

唤醒等待的线程

进入等待状态的线程可以被 pthread_cond_broadcast() 函数和 pthread_cond_signal()函数唤醒。这两个函数定义如下:

#include <pthread.h>

//唤醒所有等待的线程
int pthread_cond_broadcast(pthread_cond_t* cond);

//唤醒所有等待的线程中的一个
int pthread_cond_signal(pthread_cond_t* cond); 

参数说明

  • cond:指向要发送信号的条件变量的指针。

返回值

  • 如果函数执行成功,则返回值为 0。
  • 如果函数执行失败,则返回非0值,表示相应错误。

条件变量属性

pthread_condattr_t 结构体用来描述条件变量的属性。该结构体是一个不透明结构体,其具体定义通常是由系统实现。开发人员通过系统提供的API进行操作。

初始化和销毁:

#include <pthread.h>

int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_destroy(pthread_condattr_t *attr);

时钟类型:

#include <pthread.h>

//设置时钟类型
int pthread_condattr_setclock(pthread_condattr_t *attr, 
                              clockid_t clock_id);
                              
//获取时钟类型                         
int pthread_condattr_getclock(const pthread_condattr_t *attr, 
                              clockid_t *clock_id);

时钟类型是指等待超时时间参考的时钟,有以下两种取值:

  • CLOCK_REALTIME:默认值,基于系统实时时钟。等待时间是绝对时间。是从1970-1-1开始到现在的秒数,该值可能不是单调递增的(时间可以修改)。
  • CLOCK_MONOTONIC:基于系统单调时钟。等待时间是相对时间。是指系统启动后流逝的时间。该值一定是递增的。

示例

简单的生产者-消费者模型

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 #include <time.h>
 5 #include <unistd.h>
 6 
 7 static pthread_mutex_t mutex;
 8 static pthread_cond_t cond;
 9 static int condVal = 0;
10 
11 void *threadComsumer(void *arg)
12 {
13     char name = (char)arg;
14     printf("comsumer %c start work\n", name);
15     while (1)
16     {
17         pthread_mutex_lock(&mutex);
18         while (condVal <= 0)
19         {
20             pthread_cond_wait(&cond, &mutex);
21         }
22         int count = (rand() % 3) + 1;
23         if(count > condVal)
24             count = condVal;
25         condVal -= count;
26         printf("comsumer %c get %d stuff, cur total stuff : %d\n", name, count, condVal);
27         pthread_mutex_unlock(&mutex);
28         usleep(500 * 1000);     //500ms
29     }
30     return NULL;
31 }
32 
33 void *threadproductor(void *arg)
34 {
35     printf("productor start work\n");
36     while (1)
37     {
38         pthread_mutex_lock(&mutex);
39         int count = (rand() % 5) + 1;
40         condVal += count;
41         printf("produce %d stuff\n", count);
42         pthread_mutex_unlock(&mutex);
43         if (count > 0)
44             pthread_cond_broadcast(&cond);
45         sleep(2);
46     }
47     return 0;
48 }
49 
50 int main(int argc, char **argv)
51 {
52     srand(time(0));
53     pthread_t th_arr_comsumer[5];
54     pthread_t th_productor;
55     pthread_mutex_init(&mutex, NULL);
56     pthread_cond_init(&cond, NULL);
57     pthread_create(&th_productor, NULL, threadproductor, NULL);
58 
59     for (int i = 0; i < 5; ++i)
60         pthread_create(&th_arr_comsumer[i], NULL, threadComsumer, 'A' + i);
61     
62     pthread_join(th_productor, NULL);
63     for (int i = 0; i < 5; ++i)
64         pthread_join(th_arr_comsumer[i], NULL);
65     
66     pthread_cond_destroy(&cond);
67     pthread_mutex_destroy(&mutex);
68     return 0;
69 }

输出:

producer start work
produce 4 stuff
comsumer A start work
comsumer A get 2 stuff, cur total stuff : 2
comsumer B start work
comsumer B get 2 stuff, cur total stuff : 0
comsumer C start work
comsumer D start work
comsumer E start work
produce 5 stuff
comsumer D get 1 stuff, cur total stuff : 4
comsumer E get 2 stuff, cur total stuff : 2
comsumer A get 2 stuff, cur total stuff : 0
produce 4 stuff
comsumer C get 3 stuff, cur total stuff : 1
comsumer B get 1 stuff, cur total stuff : 0
produce 5 stuff
comsumer D get 1 stuff, cur total stuff : 4
comsumer A get 1 stuff, cur total stuff : 3
comsumer B get 2 stuff, cur total stuff : 1
comsumer E get 1 stuff, cur total stuff : 0
...

 

 
 
 
posted @ 2024-03-10 22:57  西兰花战士  阅读(2)  评论(0编辑  收藏  举报