Posix多线程系列文章:
Linux 多线程Posix详解(一) : 线程的创建、等待、退出、取消与清理
Linux 多线程Posix详解(二) : 线程的同步与互斥
Linux 多线程Posix详解(三) : C++封装
文章目录
前言
不论是子线程还是主线程,实质上都共同属于同一个进程,这大大的减小了系统的开销。
另外,多线程与多进程的使用实质上主要掌握 创建
等待
退出
这三个点
创建 | 等待 | 退出 | |
---|---|---|---|
多进程 | fork | wait | exit |
多线程 | pthread_create | pthread_join | pthread_exit |
同时,多线程也有自己的线程清理函数 pthread_cleanup_push(pop)
.
一.线程创建 pthread_create
int pthread_create(pthread_t *__restrict__ __newthread,
const pthread_attr_t *__restrict__ __attr,
void *(*__start_routine)(void *),
void *__restrict__ __arg)
- 第一个参数:传入pthread_t 创建的线程的地址;
- 第二个参数:_attr, 线程的属性地址;
- 第三个参数:线程的操作函数地址;
- 第四个参数:传入函数中的参数地址;
线程创建的过程中有可能需要向子线程传入一些参数类型,那么如何进行传入参数也是多线程的难点之一。
注意:因为创建子线程并执行函数需要一定的时间,但是主线程却会一直往下执行,有可能主线程已经执行完成了,但是子线程却都还没有创建好!
- 无输入参数
可以看出,主线程与子线程属于同一个进程
void* threadFunc1(void* arg)
{
cout << "this is child thread, process id = " << getpid() << " , thread id = " << pthread_self() << endl;
pthread_exit(NULL);
}
void test1()
{
pthread_t pid;
cout << "this is main thread, process id = " << getpid() << " , thread id = " << pthread_self() << endl;
pthread_create(&pid, NULL, threadFunc1, NULL);
cout << "child thread id = " << pid << endl;
pthread_join(pid, NULL);
}
2.输入int型参数
void* threadFunc2(void* arg)
{
cout << "child thread get val:" << *(int*)arg << endl;
pthread_exit(NULL);
}
void test2()
{
pthread_t pid;
int val = 5;
pthread_create(&pid, NULL, threadFunc2, (void*)&val);
pthread_join(pid, NULL);
}
3.输入fd文件描述符
void* threadFunc3(void* arg)
{
int fd = *(int*)arg;
cout << "child thread get fd:" << fd << endl;
close(fd);
pthread_exit(NULL);
}
void test3()
{
pthread_t pid;
int fd = open("file", O_RDWR | O_CREAT, 0665);
pthread_create(&pid, NULL, threadFunc3, &fd);
pthread_join(pid, NULL);
}
4.传入char*类型
void* threadFunc4(void* arg)
{
char* buf = (char*)arg;
cout << "child thread is processing buf " << endl;
buf = (char*)malloc(10 * sizeof(char));
strcpy(buf, "hello");
cout << "now buf is " << buf << endl;
pthread_exit(NULL);
}
void test4()
{
pthread_t pid;
char *buf = NULL;
pthread_create(&pid, NULL, threadFunc4, buf);
pthread_join(pid, NULL);
}
5.输入long型参数
此处传入long型参数的方式也可以按照第2种传入的方式来,更为通用
void* threadFunc5(void* arg)
{
cout << "child thread get val:" << (long)arg << endl;
pthread_exit(NULL);
}
void test5()
{
pthread_t pid;
long val = 5;
pthread_create(&pid, NULL, threadFunc2, (void*)val);
pthread_join(pid, NULL);
}
6.传入结构体
传入学生的姓名,成绩,并打印
struct Student
{
const char* name;
double score;
};
void *threadFunc6(void* arg)
{
Student *stu = (Student*)arg;
for (int i = 0; i < 3; i++)
cout << stu[i].name << ":" << stu[i].score << endl;
pthread_exit(NULL);
}
void test6()
{
Student stus[3] = {{"wwx", 99}, {"wk", 77}, {"wby", 88}};
pthread_t pid;
pthread_create(&pid, NULL, threadFunc6, stus);
pthread_join(pid, NULL);
}
7.小练习:主线程与子线程各自加一千万,看结果是否为两千万
const int N = 10000000;
void *threadFunc7(void* arg)
{
for (int i = 0; i < N; i++)
*(int*)arg += 1;
pthread_exit(NULL);
}
void test7()
{
int sum = 0;
pthread_t pid;
pthread_create(&pid, NULL, threadFunc7, &sum);
for (int i = 0; i < N; i++)
sum ++;
pthread_join(pid, NULL);
cout << "final sum = " << sum << endl;
}
二.线程等待与退出 pthread_join ,pthread_exit
首先需要知道,什么情况下线程或进程会终止?
:
1):任意一处地方使用exit(), 进程终止;
2):main中执行到return,主线程终止, 子线程也立刻终止;
但如果是只是想要让某一个线程显式的终止,就应该调用pthread_exit函数。
再来了解 pthread_exit 线程退出函数:
void pthread_exit(void *__retval)
1):如果在主线程中调用了pthread_exit,那么只是主线程会终止,子线程依旧会持续进行。
2):如果在主线程中没有调用pthread_exit(), 那么主线程就最终会调用return,最终导致整个进程一起终止。
线程等待函数很好理解,用于阻塞等待指定的函数:
int pthread_join(pthread_t __th, void **__thread_return)
pthread_join是线程阻塞
函数,第一个参数指明等待的是哪一个线程,第二个参数是接收等待线程的资源。用法与多进程的 wait()
相同。
注意: void类型是属于万金油类型,任意的数据类型都可以转换成为void类型,只需要在接收数据之后转换为原类型就可以了。
1.传出long/int型
void *threadFunc1(void *arg)
{
long val = 4;
pthread_exit((void *)val); //此处不能使用 (void*)&val ,因为这样传出的是地址,而不是值
}
void test1()
{
pthread_t pid;
pthread_create(&pid, NULL, threadFunc1, NULL);
long val;
pthread_join(pid, (void **)&val);
cout << "val = " << val << endl;
}
- 传出char*型
void *threadFunc2(void *arg)
{
char *s = "HelloWorld";
pthread_exit(s);
}
void test2()
{
pthread_t pid;
pthread_create(&pid, NULL, threadFunc2, NULL);
void *buf;
pthread_join(pid, &buf);
cout << (char *)buf << endl;
}
三.线程的取消与清理 pthread_canel , pthread_cleanup_push(pop)
int pthread_cancel(pthread_t __th)
取消子线程函数,此时资源必须手动清理.
void pthread_cleanup_push(void (*routine)(void *), void* arg)
void pthread_cleanup_pop(int execute)
以上为两个资源清理函数,必须成对出现。
execute值为0的情况下,只有调用pthread_exit() 或有其他线程使用pthread_cancel() 才能调用清理函数
void cleanup(void *arg)
{
free(arg);
cout << "thread cleanup" << endl;
}
void *threadFunc(void *arg)
{
cout << "child thread is running " << endl;
int *a = (int *)malloc(8);
pthread_cleanup_push(cleanup, a);
sleep(10);
cout << "after sleep()" << endl;
pthread_exit(NULL);
pthread_cleanup_pop(0);
}
int main()
{
pthread_t pid;
pthread_create(&pid, NULL, threadFunc, NULL);
sleep(1);
pthread_cancel(pid);
pthread_join(pid, NULL);
return 0;
}