在信号量测试时,常常需要创建多个线程,比如生产者-消费者问题中,需要模拟多个生产者、多个消费者。
pthreads是指POSIX threads。
1. 一个简单例子
创建10个线程,每个线程打印出自己的线程ID,
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
#define NUM_THREADS 10
void *do_something(void *arg)
{
printf("%d \t Thread ID %ld\n", *(int *)arg, pthread_self());
sleep(2);
}
int main()
{
pthread_t thread_id[NUM_THREADS];
for (int i=0; i<NUM_THREADS; i++){
int retval = pthread_create(&thread_id[i], NULL, do_something, &i);
if (0 != retval){
printf("%d \t return value: %d\n", i, retval);
perror("pthread_create error.");
return -1;
}
}
// wait for the threads to terminate
for (int i=0; i<NUM_THREADS; i++){
if (0 != pthread_join(thread_id[i], NULL)){
perror("pthread_join error.");
}
}
return 0;
}
2. 线程创建pthread_create
线程创建函数:pthread_create(3) - Linux manual page
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
pthread_t *thread
,线程创建后返回的线程ID,类型实为unsigned long int
void *(*start_routine) (void *)
,是一个函数指数,线程执行的代码void *arg
,传给线程函数start_routine
的参数,只能一个
pthread_create
创建的线程默认是joinable,可以调用pthread_join
函数。
3. pthread_join
pthread_join(3) - Linux manual page
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
等待线程thread
终止,retval
是线程thread
的返回值。
(1)如果没有pthread_join
如果主线程没有调用pthread_join
,试想这么一种情况,新创建的线程需要进行耗时的计算操作,那么主线程退出了,新创建的线程尚未运行结束。
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
#define NUM_THREADS 10
void *do_something(void *arg)
{
printf("%d \t Thread ID %ld\n", *(int *)arg, pthread_self());
sleep(2);
}
int main()
{
pthread_t thread_id[NUM_THREADS];
for (int i=0; i<NUM_THREADS; i++){
int retval = pthread_create(&thread_id[i], NULL, do_something, &i);
if (0 != retval){
printf("%d \t return value: %d\n", i, retval);
perror("pthread_create error.");
return -1;
}
}
return 0;
}
运行结果如下,
root@jmu-cs:~/os_exp/exp5_mutex_pv_log_bridge# ./pthread_example
4 Thread ID 140002933835520
8 Thread ID 140002875086592
9 Thread ID 140002866693888
5root@jmu-cs:~/os_exp/exp5_mutex_pv_log_bridge# ./pthread_example
4 Thread ID 139747523970816
5 Thread ID 139747490400000
6 Thread ID 139747482007296
6 Thread ID 139747498792704
9 Thread ID 139747456829184
root@jmu-cs:~/os_exp/exp5_mutex_pv_log_bridge# ./pthread_example
4 Thread ID 140158325679872
5 Thread ID 140158292109056
6 Thread ID 140158283716352
可以看见,创建了10个线程,但并没有每个进程都得到运行,这是因为主线程运行结束退出了。除此之外,每次运行的结果还不同(取决于调度)。
(2)加上pthread_join
现在来看下,加上pthread_join
,会怎样,在上述代码return 0;
之前加上,
// wait for the threads to terminate
for (int i=0; i<NUM_THREADS; i++){
pthread_join(thread_id[i], NULL);
}
运行结果如下,每个线程都得到了执行。
root@jmu-cs:~/os_exp/exp5_mutex_pv_log_bridge# ./pthread_example
4 Thread ID 139964125910784
9 Thread ID 139964075554560
5 Thread ID 139964117518080
10 Thread ID 139963983193856
6 Thread ID 139964100732672
6 Thread ID 139964083947264
7 Thread ID 139964092339968
10 Thread ID 139963991586560
5 Thread ID 139964109125376
10 Thread ID 139964067161856
注:创建线程后,线程如果没有被pthread_join
,线程结束,会有一部分资源没有被回收。
4. pthread_detach
pthread_detach(3) - Linux manual page
#include <pthread.h>
int pthread_detach(pthread_t thread);
调用pthread_detach
,thread
会被标记为detached
,新线程结束后,资源会自动释放。
调用pthread_join
,新进程没有运行结束,调用者会被阻塞,有些应用并不希望如此。举例,计算机网络中,服务器通常有一个主线程监听某个端口,接收请求,并创建一个线程去处理该请求,这种情况,主线程并不希望阻塞在处理请求的进程上。应对这种情况,可以调用pthread_detach
,将新线程剥离出来。
5. 获取线程的ID
获取线程ID函数:pthread_self(3) - Linux manual page
#include <pthread.h>
pthread_t pthread_self(void);
6. 终止线程pthread_exit
pthread_exit(3) - Linux manual page
#include <pthread.h>
noreturn void pthread_exit(void *retval);
终止调用者线程。