linux下的线程创建相关API函数
线程创建相关API函数
pthread _create(创建线程函数)
1. 头文件
#include <pthread.h>
2. 函数原型
POSIX通过pthread_create()函数创建线程,API定义如下:
int pthread_create((pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库
3. 参数说明
>第一个参数为指向线程标识符的指针。
- 当创建线程成功,线程ID由第一个参数返回。
- 第二个参数用来设置线程属性attr
- 第三个参数是线程运行函数的起始地址。
- 第三个参数是线程的入口地址,线程是一个执行路线, 是进程中的单独控制序列,控制序列的入口地址由第三个参数指定。第三个参数是一个函数指针,函数指针所指向的函数特征如下,接收一个无类型的参数,返回的也是无类型指针。
- 第四个参数是运行函数的参数。
- 第四个参数为传递给线程函数的参数,也就是第三个参数所指向函数的参数。尽管arg是**void ***类型的变量,但它同样可以作为任意类型的参数传给start_routine()函数;同时,**start_routine()**可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由**pthread_join()**获取。
- 如果需要向**start_routine**函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为**arg**的参数传入。
struct fun_para
{
var para1;//参数1
var para2;//参数2
.......
}
//将这个结构体指针,作为void *形参的实际参数传递
struct fun_para para;
pthread_create(&ntid, NULL, thr_fn,¶);
//接着在线程的调用函数thr_fn中可以通过下面的方式使用通过para传入的参数。
void *thr_fn(void *arg)
{
fun_para *para;
para = (fun_para *) arg;
para->para1;//参数1
para->para2;//参数2
......
//pthread_exit(0);
return ((void *)0);
}
4. 返回值:
成功,返回0;出错,返回-1。
5. 举例说明:
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
pthread_t t_id;
void printf_tid_pid(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid(); //Linux 环境编程中用于定义进程 ID
tid = pthread_self();//获取线程ID号
printf("%s pid %u tid %u (0x%x)\n",s,(unsigned)pid,(unsigned)tid,(unsigned)tid);
}
void *thread_fuction(void *arg)
{
printf_tid_pid("new thread: ");
return ((void*)0);
}
int main(int argc,int argv)
{
int err;
t_id = pthread_self();
printf("%s %u \n","t_id: ",(unsigned)t_id);
err = pthread_create(&t_id,NULL,thread_fuction,NULL);
if(err != 0 )
{
printf("create thread fail: %s\n",strerror(err));
}
printf_tid_pid("main thread:");
sleep(1);//注释掉,线程没有执行,程序就终止了,所以需要mian进程需要sleep
return 0;
}
6、传递不同参数
传递单个参数:
#include <iostream>
#include <pthread.h>
#include<unistd.h>
using namespace std;
void* thr_fn(void* arg)
{
int i = *(int*)arg;
cout << i << endl;
return ((void*)0);
}
int main()
{
pthread_t tid;
int j = 2;
pthread_create(&tid, NULL, thr_fn, &j);
sleep(1);
return 0;
}
传递多个参数:
#include <iostream>
#include <pthread.h>
#include<unistd.h>
using namespace std;
struct parameter
{
char a;
int i;
float f;
};
void* thr_fn(void* arg)
{
struct parameter *p =(parameter*)arg;
cout << p->a << endl;
cout << p->i << endl;
cout << p->f << endl;
return ((void*)0);
}
int main()
{
pthread_t tid;
struct parameter *par = new parameter;
par->a = 'c';
par->i = 2;
par->f = 3.14;
pthread_create(&tid, NULL, thr_fn, (void*)par);
sleep(1);
return 0;
}
pthread_join()
1. 含义
使一个线程等待另一个线程结束,通过pthread_join函数会让主线程阻塞,直到所有线程都已经退出
2. 背景
如果没有pthread_join;主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
3. 函数原型
int pthread_join(pthread_t thread, void **value_ptr);
- 参数说明:
thread:等待退出线程的线程号。
value_ptr:用户定义的指针,它可以用来存储被等待线程的返回值
- 返回值
成功:0;失败:错误号 strerror函数
4. 第二个参数说明
void **value_ptr
二级指针其实在函数内部是承接了上个线程的返回值。
在使用时要注意的是:针对value_ptr,应该先声明一个一级指针,然后通过取地址的方式传给pthread_create函数,而不应该直接定义一个二级指针,将这个二级指针直接传递给pthread_create。
正确的传递方法:
void *ret;
pthread_join(thread, &ret);
错误的传递方法:
void **ret;
pthread_join(thread, ret);
原因:
pthread_join中有一句类型这样的语句:
(* value_ptr) = arg ;
如果按照正确的方式传递参数,左边的语句实际上完成的操作是:
ret = arg;
当按照错误的方式传递参数时,会出现一句致命的错误:(* value_ptr),它对一个还没有初始化的指针进行了取值操作,这是系统所不允许的,此时(* value_ptr)等价于*ret,ret还是一个指针,是一个二级指针,但是ret是一个野指针!
注意啊!你返回的是一个一级指针,那么存储就只能是用 * &ret 来存储(希望你懂,就是指针降级)=ret;那么输出自然也就是 ret了。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#define N 64
int a = 10;
void * handler(void *arg)
{
printf("a=%d, %s\n", a, (char *)arg);
strcat((char *)arg, " ......");
pthread_exit(arg);
}
int main()
{
pthread_t tid;
char buf[N] = {"welcome"};
void *result;
if (pthread_create(&tid, NULL, handler, (void *)buf) != 0)
exit(-1);
printf("*\n");
pthread_join(tid, &result);
printf("ret:%s\n", (char *)result);
return 0;
}
如果:
void **result;
pthread_join(tid, result);
5. 终止状态(线程返回值)
通过pthread_join得到的终止状态是不同的,总结如下:
- 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值(即void *指针);
- 如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。
- 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数(即void *指针),其实1与3是等效的。
- 如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
void *thread_function(void *arg) {
int i;
for ( i=0; i<8; i++)
{
printf("Thread working...! %d \n",i);
sleep(1);
}
return NULL;
}
int main(void) {
pthread_t mythread;
if ( pthread_create( &mythread, NULL, thread_function, NULL) )
{
printf("error creating thread.");
abort();
}
if ( pthread_join ( mythread, NULL ) )
{
printf("error join thread.");
abort();
}
printf("thread done! \n");
exit(0);
}
如果去掉 pthread_join()
6、回收子线程的返回值
一个线程的结束,有两种方式,一种是正常结束。一种是使用pthread_exit。对于使用pthread_exit结束的线程,可以返回一个"status"给主线程。
void pthread_exit(void* retval);
那么它的参数就和pthread_join第二个参数就对应上了
pthread_exit返回的是一种状态,那么如何获取这个返回的状态?
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* StartFunction(void* arg)
{
int a =10;
pthread_exit((void*)a);
}
int main(int argc, char* argv[])
{
pthread_t my_pthread_t ;
if (0 != pthread_create(&my_pthread_t, NULL, StartFunction, NULL))
{
printf("pthread_create error!\n");
return -1;
}
void* p = NULL;
if (0 != pthread_join(my_pthread_t, &p))
{
printf("pthread_join error!\n");
return -1;
}
printf("ret = %d\n", p);
return 0;
}
注意:
- 子线程的返回值为void *指针,其指针所指向的地址必须位于全局区或者malloc分配的空间,不能位于用户栈空间;
- 之所以子线程的返回值以及线程函数ftn的形参都要为void *类型,这是为了方便可以传递任何数据类型;
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
struct my_threadfunc_error
{
int a;
int b;
};
void* StartFunction(void* arg)
{
my_threadfunc_error var{1,2};//系统栈空间
pthread_exit((void*)&var);
}
int main(int argc, char* argv[])
{
pthread_t my_pthread_t = 0;
if (0 != pthread_create(&my_pthread_t, NULL, StartFunction, NULL))
{
printf("pthread_create error!\n");
return -1;
}
void* p = NULL;
if (0 != pthread_join(my_pthread_t, &p))
{
printf("pthread_join error!\n");
return -1;
}
my_threadfunc_error* var = (my_threadfunc_error*)(p);
printf("var.a = %d, var.b = %d\n", var->a, var->b);
return 0;
}
上面代码在线程函数中返回的是一个栈上的变量的地址,所在在代码运行到my_threadfunc_error* var = (my_threadfunc_error*)(*p);/的时候,因为线程函数结束,线程的栈也会被回收,p对象已经被释放,自然var结构体变量中的两个数据成员没有初始化,系统随机产生随机数。
所以:
指针所指向的地址必须位于全局区或者malloc分配的空间,不能位于用户栈空间;
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
struct my_threadfunc_error
{
int a;
int b;
};
void* StartFunction(void* arg)
{
my_threadfunc_error* var = (my_threadfunc_error*)malloc(sizeof(my_threadfunc_error));
var->a = 100;
var->b = 200;
pthread_exit((void*)var);
}
int main(int argc, char* argv[])
{
pthread_t my_pthread_t = 0;
if (0 != pthread_create(&my_pthread_t, NULL, StartFunction, NULL))
{
printf("pthread_create error!\n");
return -1;
}
void* p = NULL;
if (0 != pthread_join(my_pthread_t, &p))
{
printf("pthread_join error!\n");
return -1;
}
my_threadfunc_error* var = (my_threadfunc_error*)(p);
printf("var.a = %d, var.b = %d\n", var->a, var->b);
free(var);
var = NULL;
return 0;
}
pthread_detach()
1、背景
【转】在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。
pthread_detach()即主线程与子线程分离,资源在线程函数退出时或pthread_exit时自动会被释放
2、函数原型
int pthread_detach(pthread_t tid);
3、返回值
- 若成功则返回0,若出错则为非零。
- 如果检测到以下任一情况,pthread_detach()将失败并返回相应的值。
- EINVAL:tid是分离线程
- ESRCH:tid不是当前进程中有效的为分离线程
4、detached状态
(1)在线程创建时将其属性设为分离状态(detached);
unjoinable属性可以在pthread_create时指定
pthread_t pid;
int rc;
pthread_attr_t attr;
pthread_t thread;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
rc = pthread_create(&pid, &attr, PrintHello, NULL);
pthread_attr_destroy (&attr);
(2)在线程创建后将其属性设为分离的(detached)
在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。
5、pthread_join和pthread_detach小实验对比
1. pthread_detach小实验
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
void pthread1(void);
pthread_t thread;
void create_pthread(void)
{
printf("create pthread1:\n");
pthread_create(&thread,NULL,(void *)&pthread1,NULL);
pthread_detach(thread);
}
void pthread1(void)
{
while(1)
{
printf("-----starting pthread1-------\n");
sleep(1);
}
return;
}
int main(void)
{
printf("-----starting main pthread-------\n");
create_pthread();
//sleep(5);
//pthread_join ( thread, NULL );
printf("end main pthread\n");
return 0;
}
可以发现当主进程运行到
printf("end main pthread\n");
以后,由于线程是分离状态,并且sleep(1);
pthread_detach(thread);
void pthread1(void)
{
while(1)
{
printf("-----starting pthread1-------\n");
sleep(1);
}
return;
}
仍会在整个程序结束前,输出子线程的运行结果。如果子线程中:
sleep(1);注释掉的话,相当于子线程还没开始运行,主线程就结束了。
2. pthread_join小实验
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
void pthread1(void);
pthread_t thread;
void create_pthread(void)
{
printf("create pthread1:\n");
pthread_create(&thread,NULL,(void *)&pthread1,NULL);
//pthread_detach(thread);
}
void pthread1(void)
{
while(1)
{
printf("-----starting pthread1-------\n");
sleep(1);
}
return;
}
int main(void)
{
printf("-----starting main pthread-------\n");
create_pthread();
//sleep(5);
pthread_join ( thread, NULL );
printf("end main pthread\n");
return 0;
}
由于线程执行的函数为
void pthread1(void)
{
while(1)
{
printf("-----starting pthread1-------\n");
sleep(1);
}
return;
}
并且,主线程和线程是不分离的,则一直循环下去。
pthread_concel()
1. 定义
- 一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。
- 线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。
- 线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
2、函数原型
int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。
void pthread_testcancel(void)
检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。
int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和 PTHREAD_CANCEL_DISABLE,分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为 NULL则存入原来的Cancel状态以便恢复。
3、应用举例
//https://www.cnblogs.com/xiedan/archive/2009/12/16/1625977.html
#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
pthread_t pid[3];
void* thread_run_1(void* arg){
cout<<"Now in the thread 1"<<endl;
int sum = 0;
int state, oldstate;
state = PTHREAD_CANCEL_DEFERRED;
pthread_setcancelstate(state, &oldstate);
cout<<"oldstate is "<<(state == oldstate? "Deferred":"Async")<<endl;
//耗时间的循环
for (int i = 1; i<=INT_MAX; ++i);
cout<<"before testcancel"<<endl;
pthread_testcancel();
cout<<"after testcancel"<<endl;
cout<<"thread 1 done!"<<endl;
}
void* thread_run_2(void* arg){
cout<<"Now in the thread 2"<<endl;
pthread_cancel(pid[1]);
sleep(2);
cout<<"thread 2 done!"<<endl;
}
int main(){
pid[0] = pthread_self();
if (pthread_create(&pid[1],NULL,thread_run_1,NULL) != 0){
cout<<"error create thread 1"<<endl;
return -1;
}
if (pthread_create(&pid[2],NULL,thread_run_2,NULL) != 0){
cout<<"error create thread 2"<<endl;
return -1;
}
sleep(5);
cout<<"Main thread done!"<<endl;
}
pthread_kill()
1.定义
- 向同一进程下的另一线程发送信号,信号为0时用于检查此线程ID的线程是否存活。
- 向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。
2、函数原型
int pthread_kill(pthread_t threadId,int signal);
返回值:
- 成功:0 线程不存在:ESRCH 信号不合法:EINVAL
int kill_ret = pthread_kill(thread_id,0);
if(kill_ret == ESRCH)
printf("指定的线程不存在或者是已经终止\n");
else if(kill_ret == EINVAL)
printf("调用传递一个无用的信号\n");
else
printf("线程存在\n");
3、应用举例
//https://blog.csdn.net/qu1993/article/details/105580924
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *func(void *argv)
{
while (1) {
printf("I am thread, %ld\n", pthread_self());
sleep(1);
}
return NULL;
}
int main()
{
pthread_t t;
pthread_create(&t, NULL, func, NULL);
sleep(5);
printf("send kill to thread\n");
pthread_kill(t, SIGKILL);
sleep(10);//线程却没有实现signal处理函数,则整个进程退出。
printf("end\n");
}
参考
1、https://www.cnblogs.com/wainiwann/p/3550748.html
2、https://blog.csdn.net/computerme/article/details/52421928
3、https://www.cnblogs.com/zhangfeionline/p/5975272.html
4、https://blog.csdn.net/qq_33883085/article/details/89425923
5、https://blog.csdn.net/zeqi1991/article/details/96642285
6、https://www.cnblogs.com/mcy0808/p/7466614.html
7、https://www.cnblogs.com/xiedan/archive/2009/12/16/1625977.html
8、https://blog.csdn.net/qu1993/article/details/105580924