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
...
Linux多线程-线程同步的更多相关文章
- Linux 多线程 - 线程异步与同步机制
Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...
- Linux的线程同步对象:互斥量Mutex,读写锁,条件变量
进程是Linux资源分配的对象,Linux会为进程分配虚拟内存(4G)和文件句柄等 资源,是一个静态的概念.线程是CPU调度的对象,是一个动态的概念.一个进程之中至少包含有一个或者多个线程.这 ...
- windows lua 多线程 线程同步
今天在改一个程序,改成部分逻辑用lua写,这个程序是多线程的.将程序中部分逻辑改成lua之后,各种非法访问内存错误,各种奇奇怪怪的问题,不分时间,不分地点的出现崩溃.从调用堆栈来看,基本都是使用lua ...
- Linux/Unix 线程同步技术之互斥量(1)
众所周知,互斥量(mutex)是同步线程对共享资源访问的技术,用来防止下面这种情况:线程A试图访问某个共享资源时,线程B正在对其进行修改,从而造成资源状态不一致.与之相关的一个术语临界区(critic ...
- 多线程 - 线程同步锁(lock、Monitor)
1. 前言 多线程编程的时候,我们不光希望两个线程间能够实现逻辑上的先后顺序运行,还希望两个不相关的线程在访问同一个资源的时候,同时只能有一个线程对资源进行操作,否则就会出现无法预知的结果. 比如,有 ...
- Java多线程——线程同步
在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...
- Java基础-多线程-③线程同步之synchronized
使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...
- Linux下线程同步的几种方法
Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量和信号量. 一.互斥锁(mutex) 锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1. 初始化锁 int pthrea ...
- Java多线程 - 线程同步
多线程操作同一个对象时,容易引发线程安全问题.为了解决线程安全问题,Java多线程引入了同步监视器. 同步代码块 同步代码块语法格式如下: synchronized(obj){ //此处的代码即为同步 ...
- Java分享笔记:创建多线程 & 线程同步机制
[1] 创建多线程的两种方式 1.1 通过继承Thread类创建多线程 1.定义Thread类的子类,重写run()方法,在run()方法体中编写子线程要执行的功能. 2.创建子线程的实例对象,相当于 ...
随机推荐
- redis - [03] 配置&命令
题记部分 一.配置(Config) 二.命令(Command) (1)启动redis服务:redis-server.exe redis.windows.conf (2)连接redis-server:r ...
- Twain Capabilities属性
Asynchronous Device Events 异步设备事件 CAP_DEVICEEVENT MSG_SET选择应用程序希望Twain源报告的事件; MSG_RESET返回Twain源的首选设置 ...
- 关于JS中继承
继承,我理解就是把所有实例可能用到的属性和方法抽出来,单独放在一个"超类"中,一方面避免重复写代码,另一方面也会节省内存.如果单独用原型继承,引用值的处理是个问题 如果单独用cal ...
- RP 点归入Set And Coupling 约束创建
想用python脚本创建一个耦合coupling关系,需要定义control piont和被控制的surfaces.两者都可以先分别归入到set 和surface里,最后用set和surface作为c ...
- python excel 读取:如何读取符合多个条件的记录【出差、外出、调休、年假】
if 语句结合or 实现:读取所有出差.外出.调休.年假的记录 if '出差' in str(c_cell) or '外出' in str(c_cell) or'调休' in str(c_cell) ...
- k8s v1.19版本之后,自签证书过期x509: certificate has expired or is not yet valid
前言 在 Kubernetes 1.16 版本之前,kubeadm 工具的 alpha certs 子命令用于生成和管理 Kubernetes 集群的证书.然而,从 Kubernetes 1.19 版 ...
- cURL 工具库基本使用
cURL(Client URL)是一个功能强大的工具和库,用于与各种网络协议进行交互,cURL常用的一些参数和示例代码: -X, --request :指定HTTP请求方法(GET.POST.PUT等 ...
- php通过Curl给接口上传文件。
在 PHP 中使用 cURL 上传文件至接口,你可以通过 CURLOPT_POSTFIELDS 选项来设置文件的内容.以下是一个示例: function uploadFile($url, $fileP ...
- Netty源码—9.性能优化和设计模式
大纲 1.Netty的两大性能优化工具 2.FastThreadLocal的实现之构造方法 3.FastThreadLocal的实现之get()方法 4.FastThreadLocal的实现之set( ...
- JLabel展示文本和图片--java进阶day03
1.JLabel 我们想要在窗体中展示图片或者文本是不能直接展示的,文本和图片必须要放在JLabel这个组件中 JLabel实质是窗体中的一块区域,创建了一个JLabel对象意味着在窗体中开辟了一块区 ...