【pthread】POSIX接口简述

pthread简介

POSIX Threads简称Pthreads,此标准定义了一套C语言的类型,函数和常量。定义在pthread.h头文件和一个线程库里,大约有100多个API,可以分为4大类:

  • 线程管理:包括线程创建(create),线程分离(detach),线程连接(join)及设置和查询线程属性的函数等
  • 互斥锁:用于限制线程对共享数据的访问,保护共享数据的完整性。包括创建、销毁、锁定和解锁互斥锁及一些用于设置或修改互斥量属性等函数。
  • 条件变量:用于共享一个互斥量的线程间的通信。包括条件变量的创建、销毁、等待和发送信号等函数
  • 读写锁和屏障:包括读写锁和屏障的创建、销毁、等待及相关属性设置等函数
函数前缀 函数组
pthread_ 线程属性对象
pthread_mutex_ 互斥锁
pthread_mutexaddr_ 互斥锁属性对象
pthread_cond_ 条件变量
pthread_condattr_ 条件变量属性对象
pthread_rwlock_ 读写锁
pthread_rwlockattr_ 读写锁属性对象
pthread_spin_ 自旋锁
pthread_barrier_ 屏障
pthread_barrierattr_ 屏障属性对象
sem_ 信号量
mq_ 消息队列

pthread API - 线程

创建新的线程

函数原型

int pthread_create(pthread_t *restrict tid,const pthread_attr_t *restrict attr,
                    void *(*start_rtn)(void *),void *restrict arg);

参数说明

tid:指向想成句柄的指针,不能为NULL
attr:指向线程属性的指针,如果使用NULL,则使用默认的线程属性
start:线程入口函数地址
arg: 传递给线程入口函数的参数

线程脱离

调用此函数,如果pthread线程没有结束,则将thread线程属性的分离状态设置为detached;当thread线程已经结束时,系统将回收pthread线程占用的资源。

函数原型

int pthread_detach (pthread_t thread);

参数说明

thread : 线程句柄

使用方法

子线程调用pthread_detach(pthread_self()),或者其他线程调用pthread_detach(thread_id)。注意:线程属性的分离状态设置为detached,此线程不能被pthread_join函数等待,或者重新被设置为detached。

等待线程结束

在pthread的实现中,每个线程都有两个特性:joinable和detached,当创建一个线程的默认属性是joinable,表示线程是可以使用pthread_join进行同步的。

当一个线程调用pthread_join(T,ret),这个函数返回的时候表示线程T已经终止了,执行完成。那么就可以释放与线程T的相关的系统资源。

如果一个线程的状态是detached状态的话,当线程结束的时候与这个线程相关的资源会被自动释放掉,将资源归还给系统,也就不需要其他的线程调用pthread_join来释放线程的资源了。

此函数会调用该函数的线程以阻塞的方式等待线程分离属性为joinable的thread线程运行结束,并获得thread线程的返回值,返回值的地址保存在value_ptr里,并释放thread线程占用的资源。

函数原型

int pthread_join(pthread_t thread,void **value_ptr);

参数说明

thread:线程句柄(线程描述符)
value_ptr : 用户定义的指针,用来存储被等待线程的返回值地址,可由函数pthread_join()获取

返回值

EDEADLK:表示检测到四艘,比如两个线程都调用pthread_join函数等待对方执行
EINVAL:线程不是一个joinable的线程,比如:pthread_join一个detached线程
ESRCH:thread是一个无效线程,比如:没有create线程
0:函数调用成功

线程标识

进程ID是用pid_t表示的,而线程的ID使用pthread_t数据类型来表示的

对两个线程ID进行比较

函数原型

#include <pthread.h>

int pthread_equal(pthread_t tidl,pthread_t tid2);

参数说明

tid1:线程1句柄
tid2:线程2句柄

获取自身的线程ID

函数原型

pthread_t pthread_self(void);

线程退出

pthread线程调用此函数会终止执行,如同进程调用exit()函数一样,并返回一个指向线程返回值的指针。线程退出由线程自身发起。

如果在进程进程中任意线程调用了exit、_Exit或者_exit,那么整个进程就会终止

单个线程可以通过3种方式退出,即在不终止整个进程的情况下,停止它的控制流。

(1)线程可以简单地从启动例程中返回,返回值是线程的退出码
(2) 例程可以被同一进程中的其他线程取消
(3) 线程调用pthread_exit

函数原型

#include <pthread.h>

void pthread_exit(void *rval_ptr);

参数说明

rval_ptr: 用户定义的指针,用来存储被等待线程的返回值地址,可由函数pthread_join()获取

pthread API - 互斥锁

互斥锁初始化

函数原型

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

参数说明

mutex:互斥锁句柄,不能为NULL
attr:指向互斥锁属性的指针,若指针为NULL,则使用默认的属性

返回值

0:初始化成功
EINVAL:参数无效

销毁互斥锁

函数原型

int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数说明

mutex:互斥锁句柄,不能为NULL

返回值

0:销毁成功
EINVAL:mutex为空或mutex已销毁
EBUSY:互斥锁正在被使用

注意

当确定互斥锁没有被锁住,且没有线程阻塞在该互斥锁上,才可以销毁该互斥锁。

阻塞方式上锁

函数原型

int pthread_mutex_lock(pthread_mutex_t *mutex);

参数说明

mutex:互斥锁句柄,不能为NULL

返回值

0:成功上锁
EINVAL:参数无效
EDEADLK:线程重复调用

非阻塞方式上锁

函数原型

int pthread_mutex_trylock(pthead_mutex_t *mutex);

参数说明

mutex:互斥锁句柄,不能为NULL

返回值

0:成功上锁
EINVAL:参数无效
EDEADLK:线程重复调用
EBUSY:mutex已经被其他线程上锁返回EBUSY

注意

此函数与阻塞方式上锁的区别在于,如果互斥锁mutex已经被上锁,线程不会被阻塞,而是返回错误码

互斥锁解锁

函数原型

int pthread_mutex_unlock(pthread_mutex_t *mutex);

参数说明

mutex:互斥锁句柄,不能为NULL

返回值

0:成功上锁
EINVAL:参数无效
EPERM:

pthread API - 条件变量

条件变量其实就是一个信号量,用于线程间同步。条件变量用于阻塞一个线程,当条件满足时向阻塞的线程发送一个条件,阻塞线程就被唤醒,条件变量需要和互斥锁配合使用,互斥锁用于保护共享数据。

条件变量的主要操作包括:

调用pthread_cond_init()对条件变量初始化
调用pthread_cond_destory()销毁一个条件变量
调用pthread_cond_wait()等待一个条件变量
调用pthread_cond_signal()发送一个条件变量。

初始化条件变量

此函数会初始化cond条件变量,并根据attr指向的条件变量属性设置其属性,attr一般设置NULL使用默认值

函数原型

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

参数说明

cond:条件变量句柄,不能为NULL
attr:指向条件变量属性的指针,若为NULL则使用默认属性值

返回值

0:初始化成功
EINVAL:参数无效

销毁条件变量

销毁后cond处于未初始化状态。销毁之后条件变量的属性及控制块参数将不在有效,可以调用pthread_cond_init()或静态方式重新初始化,销毁条件变量前需要确定没有线程被阻塞在该条件变量上,也不会等待获取、发信号或者广播

函数原型

int pthread_cond_destroy(pthread_cond_t *cond);

参数说明

cond:条件变量句柄,不能为NULL

返回值

0:销毁成功
EINVAL:参数无效
EBUSY:条件变量正在使用

阻塞方式获取条件变量

此函数会以阻塞方式获取cond条件变量。线程等待条件变量前需要现将mutex互斥锁锁住,此函数首先判断条件变量是否可用,如果不可用则初始化一个条件变量,之后解锁mutex互斥锁,然后尝试获取一个信号量,当信号量值大于零时,表明信号量可用,线程将获得信号量, 也就获得该条件变量,相应的信号量值会减1。如果信号量的值等于零,表明信号量不可用,线程将阻塞直到信号量可用,之后将对mutex互斥锁再次上锁。

函数原型

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

参数说明

cond:条件变量句柄,不能为NULL
mutex:指向互斥锁控制块指针

返回值

0:成功获取条件变量
EINVAL:参数无效

指定阻塞时间获取条件变量

此函数和pthread_cond_wait()函数唯一的差别在于,如果条件变量不可用,线程将被阻塞abstime时长,超时后函数将直接返回ETIMEDOUT错误码,线程将会被唤醒进入就绪态。

函数原型

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

参数说明

cond :条件变量句柄,不能为NULL
mutex : 指向互斥锁控制块的指针,不能NULL
abstime : 指定的等待事件

返回值

0:成功获取条件变量
EINVAL:参数无效
ETIMEDOUT:超时返回

发送满足条件信号量

此函数会发送一个信号且只唤醒一个等待cond条件变量的线程,就是发送一个信号量。当信号量的值等于零,并且有线程等待这个信号量时,将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量。否则将把信号量的值加1。

函数原型

int pthread_cond_signal(pthread_cond_t *cond);

参数说明

cond :条件变量句柄,不能为NULL

返回值

0:总是成功

广播

调用此函数将唤醒所有等待cond条件变量的线程。

函数原型

int pthread_cond_broadcast(pthread_cond_t *cond);

参数说明

cond :条件变量句柄,不能为NULL

返回值

0:广播成功
EINVAL:参数无效

pthread API - 消息队列

消息队列可以接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被环形以接收并处理消息。

消息队列主要操作包括:
通过函数mq_open()创建或者打开
调用mq_send()发送一个消息到消息队列
调用mq_receive()从消息队列获取一条信息
调用mq_unlink()删除消息队列

创建或打开消息队列

根据消息队列的名字name创建一个新的消息队列或者打开一个已经存在的消息队列。 Oflag的可选值有0、O_CREAT或O_CREAT|O_EXCL。如果Oflag设置为O_CREAT则会创建一个新的消息队列。如果Oflag设置O_CREAT|O_EXCL,如果消息队列已经存在则会返回NULL,如果不存在则会创建一个新的消息队列。如果Oflag设置为0,消息队列不存在则会返回NULL。

函数原型

mqh_t mq_open(const char *name,int oflag,...);

参数说明

name : 消息队列
oflag: 消息队列打开方式

返回值

成功则返回消息队列句柄,否则返回NULL

分离消息队列

根据消息队列名称name查找消息队列,若找到,则将消息队列置为分离状态,之后若持有计数为0,则删除消息队列,并释放消息队列占有的资源。

函数原型

int mq_unlink(const char *name);

参数说明

name : 消息队列名称

返回值

成功返回0,若消息 队列不存在则返回-1

关闭消息队列

当一个线程终止时,会对其占用的消息队列执行此关闭操作。不论线程是自愿终止还是非自愿终止都会执行这个关闭操作,相当于是消息队列的持有计数减1,若减1后持有计数为0,且消息队列处于分离状态,则会删除mqdes消息队列并释放其占有的资源。

函数原型

int mq_close(mqd_t mqdes);

参数说明

mqdes:消息队列句柄

返回值

成功返回0,否则返回-1

阻塞方式发送消息

向mqdes消息队列发送一条消息,此函数把msg_ptr指向的消息添加到mqdes消息队列中,发送的消息长度msg_len必须小于或者等于创建消息队列时设置的最大消息长度,插入到 msg_prio的指定位置。

如果消息队列已经满,即消息队列中的消息数量等于最大消息数,发送消息的的线程或者中断程序会收到一个错误码(-RT_EFULL)。

函数原型

int mq_send(mqd_t mqdes,
            const char *msg_ptr,
            size_t msg_len,
            unsigned msg_prio);

参数说明

mqdes:消息队列句柄
sg_ptr:指向要发送的消息的指针,不能为NULL
msg_len:发送的消息的长度
msg_prio:消息优先级,如果不需要设置优先级,可以设置为0

返回值

成功返回0,否则返回-1

阻塞方式接收消息

把mqdes消息队列里面最老的消息移除消息队列,并把消息放到msg_ptr指向的内存里。如果消息队列为空,调用mq_receive()函数的线程将会阻塞,直到消息队列中消息可用。

函数原型

ssize_t mq_receive(mqd_t mqdes,
                        char *msg_ptr,
                        size_t msg_len,
                        unsigned *msg_prio,
                        const struct timespec (abs_timeout);

参数说明

mqdes:消息队列句柄
msg_ptr:指向要发送的消息的指针,不能为NULL
msg_len:发送的消息的长度
msg_prio:消息优先级,如果不需要设置优先级,可以设置为0
abs_timeout:指定的等待时间

返回值

成功则返回消息长度,否则返回-1

指定阻塞时间接收消息

和mq_receive()函数的区别在于,若消息队列为空,线程将阻塞abs_timeout时长,超时后函数直接返回-1,线程将被唤醒由阻塞态进入就绪态。

函数原型

ssize_t mq_timedreceive(mqd_t mqdes,
                        char *msg_ptr,
                        size_t msg_len,
                        unsigned *msg_prio,
                        const struct timespec *abs_timeout);

参数说明

mqdes:消息队列句柄,不能为NULL
msg_ptr:指向要发送的消息指针,不能为NULL
msg_len:发送的消息的长度
msg_prio:消息优先级,如果不需要设置优先级,可以设置为0
abs_timeout:指定的等待时间

返回值

成功则返回消息长度,否则返回-1

pthread API - 屏障

屏障是多线程同步的一种方法。把先后到达的多个线程同时阻拦住,直到所有线程到期,然后撤去阻拦同时放行。先到达的线程将会阻塞,等到所有调用pthread_barrie_wait()函数的线程(屏障初始化时会指定count)都到达后,这些线程就会由阻塞状态进入就绪状态再次参与系统调度。

屏障是基于条件变量和互斥锁实现的。主要操作包括:调用pthread_barrier_init()初始化一个屏障,其他线程调用pthread_barrier_wait(),所有线程到期后线程唤醒进入准备状态,屏障不在使用调用pthread_barrier_destroy()销毁一个屏障。

屏蔽控制块

创建一个屏障前需要先定义一个pthread_barrier_t 屏障控制块,定义在pthread.h的头文件中。

struct pthread_barrier
{
   int count; /*指定的等待线程个数*/
   pthread_cond_t cond; /* 条件变量 */
   pthread_mutex_t mutex; /* 互斥锁 */
};
typedef struct pthread_barrier pthread_barrier_t;

创建屏障

创建一个barrier屏障,并根据默认的参数对屏障控制块的条件变量和互斥锁初始化,初始化后指定的等待线程个数为count个,必须对应count个线程 调用pthread_barrier_wait()。attr一般设置NULL使用默认值即可

函数原型

int pthread_barrier_init(pthread_barrier_t *barrier,
                         const pthread_barrierattr_t *attr,
                         unsigned count);

参数说明

attr:指向屏蔽属性的指针,传入NULL,则使用默认值,非NULL必须使用 PTHREAD_PROCESS_PRIVATE
barrier:屏蔽句柄
count:指定的等待线程的个数

函数返回

初始化成功返回0,参数无效返回EINVAL。

销毁屏障

此函数会销毁一个barrier屏障。销毁之后屏障的属性及控制块参数将不在有效,但可以调用pthread_barrier_init()重新初始化。

函数原型

int pthread_barrier_destroy(pthread_barrier_t *barrier);

参数说明

barrier: 屏障句柄

函数返回

销毁成功返回0,参数无效返回EINVAL。

等待屏障

此函数同步等待在barrier前的线程,由每个线程主动调用,若屏障等待线程个数count不为0,count将减1,若减1后count为0,表明所有线程都已经到达栏杆前,所有到达的线程将被唤醒重新进入就绪状态,参与系统调度。若减一后count不为0,表明还有线程没有到达屏障,调用的线程将阻塞直到所有线程到达屏障。

函数原型

int pthread_barrier_wait(pthread_barrier_t *barrier);

参数说明

barrier:屏蔽句柄

返回值

等待成功返回0,参数无效返回EINVAL,死锁返回EDEADLK。

pthread API - 信号量

信号量可以用于进程与进程之前间,或者进程内线程之间的通信。每个信号量都有一个不会小于0的信号量值,对应信号量的可用数量。调用sem_init()或者sem_open()给信号量值赋初值,调用sem_post()函数可以让信号量值加1,调用sem_wait可以让信号量值减1,如果当信号量为0,调用sem_wait()的线程被挂起在该信号量的等待队列上,知道信号量值大于0,处于可用状态。

POSIX信号量分为有名信号量和无名信号量:

  • 有名信号量:其值保存在文件中,一般用于进程间同步或互斥
  • 无名信号量:其值保存在内存中,一把用于线程间同步或互斥

无名信号量

无名信号量的值保存在内存中,一般用于线程间同步会互斥。在使用之前,必须先调用sem_init()初始化。

无名信号量初始化

此函数初始化一个无名信号量sem,根据给定的或默认的参数对信号量相关数据结构进行初始化,并把信号量放入信号量链表里。初始化后信号量值为给定的初始化value.

函数原型

int sem_init(sem_t *sem,int pshared,unsigned int value);

参数说明

sem:信号量句柄
value:信号量初始值
pshared:不为0时此信号量在进程间通向,否则只能为当前进程的所有线程共享

函数返回值

初始化成功返回0,否则返回-1

销毁无名信号量

此函数会销毁一个无名信号量sem,并释放信号量占用的资源

函数原型

int sem_destroy(sem_t *sem);

参数说明

sem:信号量句柄

函数返回值

销毁成功返回0,否则返回-1

有名信号量

有名信号量的值保存在文件中,一般用于进程间同步或互斥。两个进程可以操作相同名称的有名信号量。

创建或打开有名信号量

此函数会根据信号量名字name创建一个新的信号量或者打开一个已经存在的信号量

函数原型

sem_t *sem_open(const char *name,int oflag,...);

参数说明

name:信号量名称
oflag:信号量的打开方式,O_CREAT:创建一个新的信号量,O_CREAT|O_EXCL:如果信号量已经存在则返回NULL,如果不存在则创建一个新的信号量。

函数返回值

成功则返回信号量句柄,否则返回NULL。

分离有名信号量

此函数会根据信号量名称查找该信号量,该信号量存在,则将该信号量标记为分离状态。之后检查引用计数,若为0,则立即删除信号量,若值不为0,则得到所有持有该信号量的线程关闭信号量之后才会删除。

函数原型

int sem_unlink(const char *name);

参数说明

name : 信号量名称

函数返回

分离成功返回0,若信号量不存在则返回-1。

关闭有名信号量

当一个线程终止时,会对其占用的信号量执行此关闭操作。不会线程时资源终止还是非自愿终止都会执行这个关闭操作,相当于是信号量的持有计数-1.若减一后持有计数为0且信号量已经处于分离状态,则会删除sem新海量并释放其占有的资源。

函数原型

int sem_close(sem_t *sem);

参数说明

sem:信号量句柄

函数返回

成功关闭返回0,否则返回-1。

获取信号量值

此函数可以获取sem信号量的值,并保存在sval指向的内存里,可以知道信号量的资源数量

函数原型

int sem_getvalue(sem_t *sem,int *sval);

参数说明

sem:信号量句柄,不能为NULL
sval:保存获取的信号量值地址,不能为NULL

函数返回

成功关闭返回0,否则返回-1。

阻塞方式等待信号量

线程调用此函数获取信号量,若信号量值大于零,表明信号量可用,线程获得信号量,信号量值减1。若信号量值等于0,表明信号量不可用,线程阻塞进入挂起状态,并按照先进先出的方式排队等待,直到信号量可用。

函数原型

int sem_wait(sem_t *sem);

参数说明

sem:信号量句柄,不能为NULL

函数返回

成功返回0,否则返回-1。

非阻塞方式获取信号量

此函数是sem_wait()函数的非阻塞版,是rt_sem_take(sem,0)函数的封装。当信号量不可用时,线程不会阻塞,而是直接返回。

函数原型

int sem_trywait(sem_t *sem);

参数说明

sem:信号量句柄,不能为NULL

函数返回

成功返回0,否则返回-1。

指定阻塞时间等待信号量

此函数和sem_wait()函数的区别在于,若信号量不可用,线程将阻塞abs_timeout时长,超时后函数返回-1,线程将被唤醒由阻塞态进入就绪态。

函数原型

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

参数说明

sem:信号量句柄
abs_timeout:指定的等待时间,单位是操作系统时钟节拍(OS Tick)

函数返回

成功返回0,否则返回-1。

发送信号量

此函数将释放一个sem信号量,若等待该信号量的线程队列不为空,表明有线程在等待该信号量,第一个等待该信号量的线程将由挂起状态切换到就绪状态,等待系统调度,若没有线程等待该信号量,该信号量值将加1

函数原型

int sem_post(sem_t *sem);

参数说明

sem:信号量句柄,不能为NULL

函数返回

成功返回0,否则返回-1。

实例代码

实例为生产者消费者模型。一个生产者线程和一个消费者线程对同一块内存进行操作,,生产者往共享内存填充数据,消费者从共享内存读取数据。

此程序会创建2个线程,2个信号量,一个信号量表示共享数据为空状态,一个信号量表示共享数据不为空状态,一个互斥锁用于保护共享资源。生产者线程生产好数据后会给消费者发送一个full_sem信号量,通知消费者线程有数据可用,休眠2秒后会等待消费者线程发送的empty_sem信号量。消费者线程等到生产者发送的full_sem后会处理共享数据,处理完后会给生产者线程发送empty_sem信号量。程序会这样一直循环。

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
 
/* 静态方式初始化一个互斥锁用于保护共享资源*/ 
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/* 2个信号量控制块,一个表示资源空信号,一个表示资源满信号 */
static sem_t empty_sem,full_sem;
 
/* 指向线程控制块的指针 */
static pthread_t tid1;
static pthread_t tid2;
 
/* 函数返回值检查 */
static void check_result(char* str,int result)
{
    if (0 == result)
    {
        printf("%s successfully!\n",str);
    }
    else
    {
        printf("%s failed! error code is %d\n",str,result);
    }
}
 
/* 生产者生产的结构体数据,存放在链表里 */
struct node 
{
    int n_number;
    struct node* n_next;
};
struct node* head = NULL; /* 链表头,是共享资源 */
 
/* 消费者线程入口函数 */
static void* consumer(void* parameter) 
{
    struct node* p_node = NULL;
 
    while (1)
    {
        sem_wait(&full_sem);
        pthread_mutex_lock(&mutex);    /* 对互斥锁上锁, */
 
        while (head != NULL)    /* 判断链表里是否有元素 */
        {
            p_node = head;    /* 拿到资源 */
            head = head->n_next;    /* 头指针指向下一个资源 */
            /* 打印输出 */
            printf("consume %d\n",p_node->n_number);
 
            free(p_node);    /* 拿到资源后释放节点占用的内存 */ 
        }
 
        pthread_mutex_unlock(&mutex);    /* 临界区数据操作完毕,释放互斥锁 */
 
        sem_post(&empty_sem);  /* 发送一个空信号量给生产者 */
    }    
}
/* 生产者线程入口函数 */
static void* product(void* patameter)
{
    int count = 0;
    struct node *p_node;
 
    while(1)
    {
        /* 动态分配一块结构体内存 */
        p_node = (struct node*)malloc(sizeof(struct node));
        if (p_node != NULL)
        {
            p_node->n_number = count++;    
            pthread_mutex_lock(&mutex);    /* 需要操作head这个临界资源,先加锁 */
 
            p_node->n_next = head;
            head = p_node;    /* 往链表头插入数据 */
 
            pthread_mutex_unlock(&mutex);    /* 解锁 */
            printf("produce %d\n",p_node->n_number);
 
            sem_post(&full_sem);  /* 发送一个满信号量给消费者 */
        }
        else
        {
            printf("product malloc node failed!\n");
            break;
        }
        sleep(2);    /* 休眠2秒 */
        sem_wait(&empty_sem);  /* 等待消费者发送空信号量 */
    }
}
 
int main() 
{
    int result;
 
    sem_init(&empty_sem,NULL,0);
    sem_init(&full_sem,NULL,0);
    /* 创建生产者线程,属性为默认值,入口函数是product,入口函数参数为NULL*/
    result = pthread_create(&tid1,NULL,product,NULL);
    check_result("product thread created ",result);
 
    /* 创建消费者线程,属性为默认值,入口函数是consumer,入口函数参数是NULL */
    result = pthread_create(&tid2,NULL,consumer,NULL);
    check_result("consumer thread created ",result);
 
    return 0;
}









参考文章:
UNIX环境高级编程 - 线程
https://www.bookstack.cn/read/rtthread-manual-doc/16.2.md

posted @ 2022-11-30 22:16  Emma1111  阅读(886)  评论(0编辑  收藏  举报