pthread
基础API
头文件: #include <pthread.h>
失败返回错误号, 不能用perror打印错误信息, 使用strerror(ret)
pthread_self
pthread_t pthread_self(void);
返回线程ID, 在linux下是无符号整数(%lu), 在其他系统中可能是结构体实现
线程ID是内部标识, 两个进程间, 线程ID可以相同
pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
创建线程: pthread_create, 函数调用成功返回0,
参数:
thread: 传出参数, 保存系统分配的ID
attr: 线程属性, 通常传NULL. 默认父子进程不分离, 主线程要手动释放子进程pcb, 即使用pthread_join
stat_routine: 子线程的处理函数
arg: 回调函数的参数
pthread_exit
void pthread_exit(void *retval);
单个线程退出, 主线程执行pthread_exit不影响子线程的执行
主线程执行exit或return, 则子线程都会退出
子线程执行exit, 所有线程(包括主线程)都会推出
子线程执行return和pthread_exit都是退出当前线程
参数:
retval: 必须指向全局或堆的地址空间, 不能是一个栈地址
pthread_join
int pthread_join(pthread_t thread, void **retval);
阻塞等待线程退出, 获取线程退出状态
参数:
thread: 要回收的子线程的线程id, 不是指针
reval: 读取线程退出的时候携带的状态信息, 指向的内存和pthread_exit或return参数指向的内存一样; 如果线程使用pthread_cancel异常终止, reval所指向的单元里存放常数PTHREAD_CANCELED
pthread_detach
int pthread_detach(pthread_t thread);
线程分离
调用该函数之后不需要pthread_join, 子线程会自动回收自己的pcb, 一般在创建线程时设置分离属性
pthread_cancel
int pthread_cancel(pthread_t thread);
杀死(取消)线程, 可杀死分离的线程
使用注意事项: 在要杀死的子线程对应的处理的函数的内部, 必须做过一次系统调用(使用类似printf的函数), 即不是实时的杀死线程; 可以使用pthread_testcancel()来添加取消点
pthread_equal
int pthread_equal(pthread_t t1, pthread_t t2);
比较两个线程ID是否相等(预留函数)
线程属性的设置
通过属性设置线程的分离
线程属性类型: pthread_attr attr;
线程属性操作函数
int pthread_attr_init(pthread_attr_t *attr);
对线程属性变量的初始化
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
设置线程分离属性
attr: 线程属性
detachstate: PTHREAD_CREATE_DETACHED(分离), PTHREAD_CREATE_DETACHED(非分离)
int pthread_attr_destroy(pthread_attr_t *attr);
释放线程资源
使用注意事项
- 主线程退出其他线程不退出, 主线程应调用pthread_exit
- 避免僵尸线程: pthread_join, pthread_detach, pthread_create指定分离属性
- malloc和mmap申请的内存可以被其他线程释放
- 应避免在多线程模型中调用fork, 除非马上exec, 子进程中只有调用fork的线程存在, 其他线程在子进程中均pthread_exit-->不太懂
- 信号的复杂语义很难和多线程共存, 应避免在多线程引入信号机制
实例程序
pthread_create使用
输出顺序不确定
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
void *myfunc(void * arg) {
// 打印子线程ID
printf("child thread id: %lud\n", pthread_self());
return NULL;
}
int main(int argc, const char * argv[]) {
// 创建一个子线程
// 线程ID对应的变量
pthread_t pthid;
pthread_create(&pthid, NULL, myfunc, NULL);
printf("parent thread id: %lud\n", pthread_self());
for (int i = 0; i < 5; i++) {
printf("i = %d\n", i);
}
sleep(2);
return 0;
}
传值与传地址
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
void *myfunc(void *arg) {
//int num = *(int*)arg;
int num = (int)arg;
// 打印子线程ID
printf("%dth child thread id: %lu\n", num, pthread_self());
}
int main(void) {
pthread_t pthid[5];
int i;
for (i = 0; i < 5; i++) {
//pthread_create(&pthid[i], NULL, myfunc, (void*)&i);
// 由于int存储空间和指针的存储空间相同所以可以这么传
pthread_create(&pthid[i], NULL, myfunc, (void*)i);
}
printf("parent thread id: %lu", pthread_self());
for (i = 0; i < 5; i++) // 子线程不会执行接下来的for循环
printf("i = %d\n", i);
sleep(2);
return 0;
}
主线程先退出
主线程先退出, 子线程正常执行
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
void *myfunc(void *arg) {
printf("child pthread id: %lu\n", pthread_self());
printf("child thread .....\n");
int i;
for (i = 0; i < 3; i++)
printf("child i = %d\n", i);
//return NULL;
}
int main(void) {
int i;
pthread_t thid;
int ret = pthread_create(&thid, NULL, myfunc, NULL);
if (ret != 0) {
printf("error number: %d\n", ret);
printf("error information: %s\n", strerror(ret));
}
printf("parent pthread id: %lu\n", pthread_self());
// 退出主线程, 子线程不受影响
pthread_exit(NULL);
printf("parent thread .....\n");
for (i = 0; i < 3; i++)
printf("i = %d\n", i);
return 0;
}
/*
parent pthread id: 139948367210304
child pthread id: 139948358948608
child thread .....
child i = 0
child i = 1
child i = 2
*/
join与pthread_exit
没有return返回和return NULL结果一样都是core dumped
使用return &number
和pthread_exit(&number)
时, 主线程的pthread_join(thid, (void**)&ptr);
都可以收到number的值
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
int number = 100;
void *myfunc(void *arg) {
printf("child pthread id: %lu\n", pthread_self());
printf("child thread .....\n");
number++;
int i;
for (i = 0; i < 3; i++)
printf("child i = %d\n", i);
// return NULL; // Segmentation fault (core dumped)
//return &number;
pthread_exit(&number);
}
int main(void) {
int i;
pthread_t thid;
int ret = pthread_create(&thid, NULL, myfunc, NULL);
if (ret != 0) {
printf("error number: %d\n", ret);
printf("error information: %s\n", strerror(ret));
}
printf("parent pthread id: %lu\n", pthread_self());
int *ptr;
// 阻塞等待子线程的结束, 并回收pch
pthread_join(thid, (void**)&ptr);
printf("++++++ number = %d\n", *ptr);
printf("parent thread .....\n");
for (i = 0; i < 3; i++)
printf("i = %d\n", i);
return 0;
}
/*
parent pthread id: 139819023099712
child pthread id: 139819014838016
child thread .....
child i = 0
child i = 1
child i = 2
++++++ number = 101
parent thread .....
i = 0
i = 1
i = 2
*/
两个线程数
当线程A数的数字还没有写入内存中时, 就已经失去cpu
当线程B数的数字同步到内存中时, 会导致共享的数据混乱
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#define MAX 1000
// 全局变量
int number;
// 线程处理函数
void* funcA_num(void* arg)
{
for(int i=0; i<MAX; ++i)
{
int cur = number;
cur++;
number = cur;
printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
usleep(10);
}
return NULL;
}
void* funcB_num(void* arg)
{
for(int i=0; i<MAX; ++i)
{
int cur = number;
cur++;
number = cur;
printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
usleep(10);
}
return NULL;
}
int main(int argc, const char* argv[])
{
pthread_t p1, p2;
// 创建两个子线程
pthread_create(&p1, NULL, funcA_num, NULL);
pthread_create(&p2, NULL, funcB_num, NULL);
// 阻塞,资源回收
pthread_join(p1, NULL);
pthread_join(p2, NULL);
return 0;
}
/*
number可能数的数字不足
*/
使用pthread_cancel杀死分离的线程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
void* myfunc(void* arg)
{
printf("child pthread id: %lu\n", pthread_self());
while (1) {
printf("haha\n");
sleep(2);
}
return NULL;
}
int main(int argc, const char* argv[])
{
pthread_t thid;
// 初始化线程属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 返回错误号
int ret = pthread_create(&thid, &attr, myfunc, NULL);
if(ret != 0)
{
printf("error number: %d\n", ret);
// 根据错误号打印错误信息
printf("error information: %s\n", strerror(ret));
}
printf("parent pthread id: %lu\n", pthread_self());
pthread_cancel(thid);
sleep(1);
pthread_attr_destroy(&attr);
pthread_exit(NULL);
//return 0;
}
/*
parent pthread id: 140625550829376
child pthread id: 140625542567680
haha
*/