• Linux 多线程编程


    概念

    • 原来指向main()的线程叫做主线程(main thread)
    • 使用pthread_create()创建出来的线程,叫做子线程(child thread)
    • 主/子线程只有在创建时才有区别, 创建完了就一视同仁, 都是一样的独立个体, 可以有交流、共享和私有, 但没有上下级, 这一点和多进程一样, 只有在创建的瞬间才有parent process 和child process 的区别, 创建完了就都是一样的独立个体
    • 创建完子线程之后,两个线程之间独立运行,线程的执行先后次序由OS的调度算法决定
    • 线程之间相互独立也相互影响,因为主线程结束时,会导致进程结束,进程结束时,会导致该进程的所有线程结束
    • 多个线程共享一个进程, 而一个进程只有一个输出终端, So一定要调度好, 要不有的线程输出会看不到, 最low的做法就是sleep()一下保证线程可以执行完

    模型

    $gcc  -pthread
    #include<pthread.h>
    pthread_self()/pthread_equal()            //获得ThreadID
    pthread_attr_init()/pthread_attr_setdetachstate()/…                //创建一个线程前设置		
    pthread_create()                          //创建一个线程			
    pthread_detach()pthread_setcancelstate()/pthread_setcanceltype()    //已有一个线程后设置		
    pthread_kill()                            //向线程发送一个信号		
    pthread_exit()                            //退出线程但不退出进程	
    pthread_cancel()                          //终止另一个线程			
    pthread_join()                            //等待一个线程			
    

    pthread_self()

    //返回调用线程的ID
    pthread_t pthread_self(void);
    

    pthread_equal()

    //对比两个线程ID,相等返回非0,不等返回0
    int pthread_equal(pthread_t t1, pthread_t t2);
    

    pthread_attr_init()/ pthread_attr_destroy()

    //pthread_attr_init()初始化一个线程属性对象attr,不指定attr就按默认参数进行初始化。
    //pthread_attr_destroy()销毁一个线程属性对象,销毁的操作对使用这个attr的线程有影响
    //成功返回0,失败返回error number
    int pthread_attr_init(pthread_attr_t *attr);
    int pthread_attr_destroy(pthread_attr_t *attr);
    

    更改attr对象的函数

    //成功返回0,失败返回error number
    typedef struct{
    	int					detachstate;   		//线程的分离状态		
    	int               	schedpolicy;  		//线程调度策略
    	struct sched_param	schedparam;  		//线程的调度参数
    	int           		inheritsched;  		//线程的继承性
    	int              	scope;       		//线程的作用域
    	size_t             	guardsize;   		//线程栈末尾的警戒缓冲区大小
    	int               	stackaddr_set;		//线程的栈设置
    	void *          	stackaddr;   		//线程栈的位置
    	size_t             	stacksize;    		//线程栈的大小
    }pthread_attr_t;
    
    pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate)
    pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate)
    PTHREAD_CREATE_JOINABLE	/	PTHREAD_CREAT_DETACHED
    int detachstate;
    
    pthread_attr_getdetachstate(&attr,&detachstate);
    if(PTHREAD_CREATE_JOINABLE==detachstate)
    	printf("1.PTHREAD_CREATE_JOINABLE
    ");		//default
    
    pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
    pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
    SCHED_OTHER		/	SCHED_FIFO	/	SCHED_RR
    
    pthread_attr_setsschedchedparam(pthread_attr_t *attr, const struct sched_param *param);
    pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
    struct sched_param {
    	int sched_priority;// Scheduling priority,int sched_get_priority_max/sched_get_priority_min (int policy)
    };
    
    pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
    pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
    PTHREAD_INHERIT_SCHED		/	 PTHREAD_EXPLICIT_SCHED
    
    pthread_attr_setscope(pthread_attr_t *attr, int scope);
    pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
    PTHREAD_SCOPE_SYSTEM		/	PTHREAD_SCOPE_PROCESS
    
    pthread_attr_setguardsize ( pthread_attr_t *attr, size_t guardsize );
    pthread_attr_getguardsize ( const pthread_attr_t *attr, size_t *guardsize );
    >0				/	0
    (默认1 page,当然还可以指定任意值)
    
    pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
    pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
    
    pthread_attr_setstacksize ( pthread_attr_t *attr, size_t size );
    pthread_attr_getstacksize ( const pthread_attr_t *attr, size_t *size );
    

    pthread_create()

    //这个函数的create有‘e’	//p代表POSIX
    //在调用进程中创建一个新的线程,新的线程通过激活start_routine()来开始执行,arg是start_routine()唯一的参数
    //成功返回0,失败返回error number
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    

    thread:存放新线程的ID到该参数所指向的缓冲区中, pthread_t是unsigned long int
    attr: 线程的属性, 给NULL表示默认方式
    start_routine:指定新线程的处理函数,也就是新线程启动之后需要执行的代码,线程处理函数执行完毕之后, 新线程终止
    arg: 用于给start_routine传递实参(VS on_exit()) ,这个函数只支持传递void*型的型参,实际使用需要另外定义一个目标类型的指针,将arg强制类型转换后得到正确的类型

    pthread_detach()

    //将thread表示的线程标记为detached,当一个detached的线程结束后,它占用的资源被自动释放,其他线程也不能用pthread_join()等待
    //detach一个已经被detach了的线程将导致不确定的结果
    //成功返回0,失败返回error number
    
    int pthread_detach(pthread_t thread);
    

    pthread_setcancelstate()

    //设置当前线程是否允许被cancel,成功返回0,失败返回error number					
    int pthread_setcancelstate(int state, int *oldstate);
    

    state:设置新状态

    • PTHREAD_CANCEL_ENABLE //允许取消
    • PTHREAD_CANCEL_DISABLE //不允许取消

    oldstate:用于带出设置之前的旧状态

    pthread_setcanceltype()

    //设置可取消性的类型,即当前线程何时被取消	
    //成功返回0,失败返回error number					
    int pthread_setcanceltype(int type, int *oldtype);
    

    type:设置新类型

    • PTHREAD_CANCEL_DEFERED //延迟取消
    • PTHREAD_CANCEL_ASYNCHRONOUS //立即取消

    oldtype:用于带出设置之前的旧类型

    pthread_exit()

    //结束调用线程并返回一个retval值,这个值可以被同进程中的其他线程通过pthread_join()来读取,当然,前提条件是当前线程可以被汇合/等待
    void pthread_exit(void *retval);
    

    线程的3种终止方式:

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

    pthread_cancel()

    //发送一个结束请求给一个线程,是否取消以及何时取消取决于线程的属性:state和type
    //成功返回0,失败返回error number
    int pthread_cancel(pthread_t thread);
    

    pthread_join()

    //等待thread指定的线程终止,如果目标线程没有终止,则当前线程进入阻塞状态,当目标线程终止时,该函数立即返回,前提:目标线程可以被汇合/等待
    //如果调用pthread_join()的线程被cancel了,目标线程互保持joinable(不会被撤销)
    //如果多个线程同时join一个线程,那么结果是不确定的
    //成功返回0,失败返回error number
    int pthread_join(pthread_t thread, void **retval);
    

    thread:线程编号
    retval:二级指针的返回值
    retval

    • 不是NULL,将拷贝目标线程的状态信息到*retval
    • 如果目标线程被cancel了,则将PTHREAD_CANCELED防止在*retval

    例子

    //02join.c, 使用pthread_join等待目标线程结束
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<string.h>		//strerror()//没有这个.h会把strerror(error)当int
    
    void* Calc(void* pv){
    	printf("primeter is %lg, area is:%lg
    ",2*3.14**(int*)pv,3.14**(int*)pv**(int*)pv);
    	return NULL;	//要求有返回值,所以返回NULL
    }
    main(){
    	int* piRadius=(int*)malloc(sizeof(int));
    	printf("please input radius
    ");
    	scanf("%d",piRadius);
    	pthread_t thread;
    	int error=pthread_create(&thread,NULL,Calc,(void*)piRadius);
    	if(0!=error)
    		printf("%s
    ",strerror(error)),exit(-1);
    	error=pthread_join(thread,NULL);
    	if(0!=error)
    		printf("%s
    ",strerror(error)),exit(-1);
    	free(piRadius);
    	piRadius=NULL;
    	return 0;
    } 
    
    
    //03join.c  使用pthread_join获取目标线程的退出状态
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<pthread.h>
    void* task(void* pv){
    	char* pc="hello";
    	return (void*)pc;
    }
    main(){
    	//创建子线程,使用pthread_create
    	pthread_t thread;
    	int error=pthread_create(&thread,NULL,task,NULL);
    	if(0!=error)
    		printf("%s",strerror(error)),exit(-1);
    	//等待子线程结束,并获取退出状态信息
    	char* ps=NULL;
    	error=pthread_join(thread,(void**)&ps);
    	if(0!=error)
    		printf("pthread_join %s
    ",strerror(error)),exit(-1);
    	printf("child thread returned:%s
    ",ps);
    	return 0;
    } 
    
    //使用pthread_create()创建新线程
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<pthread.h>
    
    void* task(void* pv){		//需要一个实参,我们又不需要,就可以给个NULL
    	//判断thread中的线程ID是否与pthread_self()的线程ID相等
    	int res=pthread_equal(*(pthread_t*)pv,pthread_self());
    	if(0!=res)
    		printf("these two are Equal
    ");
    	else
    		printf("these two are Unequal
    ");
    	int i=0;
    	for(i=0;i<10;i++){
    		printf("I am the new thread
    ");
    		sleep(1);
    	}
    }
    int main(){
    	//1.准备存储线程编号的变量
    	pthread_t thread;
    
    	//2.创建新线程,使用pthread_create()
    	int errno=pthread_create(&thread,NULL,task,(void*)&thread);//子线程执行完task()就结束了,使用arg把thread传入子线程
    	if(0!=errno)
    		printf("pthread_create:%s
    ",strerror(errno)),exit(-1);
    	sleep(10);
    	printf("child thread's ID:%lu,parent thread's ID:%lu
    ",thread,pthread_self());//pthread_self(),获取当前线程自己的线程编号
    	return 0;
    }
    
    //使用pthread_create()创建子线程,在线程处理函数中计算1~100之间的和,保存在sum中,返回该变量的地址,主线程等待子线程结束,并获取退出状态信息,并打印
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<pthread.h>
    void* Sum(void* pv){
    	int i=0,sum=0;
    	for(i=1;i<=100;i++)
    		sum+=i;
    	printf("%d
    ",sum);
    	return (void*)"success";	//其实return "success"就行
    }
    int main(){
    	pthread_t thread;
    	int res=pthread_create(&thread,NULL,Sum,NULL);
    	if(0!=res)
    		printf("%s",strerror(res)),exit(-1);
    	char *retval=NULL;
    	res=pthread_join(thread,(void**)&retval);
    	if(0!=res)
    		printf("%s
    ",strerror(res)),exit(-1);
    	printf("child thread return : %s
    ",retval);
    	return 0;
    }
    
    //在线程处理函数中打印1~20之间的函数,当打印到10时终止当前进程并带出10,主线程等待并获取退出状态信息,打印最终结果
    #include<stdio.h>
    #include<pthread.h>
    void* task(void* pv){
    	static int i=0;
    	for(i=1;i<=20;i++){
    		if(10==i)
    			pthread_exit((void*)&i);	//pthread_exit()  VS  exit()
    		else
    			printf("i=%d
    ",i);
    	}
    	return NULL;
    }
    main(){
    	pthread_t thread;
    	pthread_create(&thread,NULL,task,NULL);
    	int *retval=NULL;
    	pthread_join(thread,(void**)&retval);		//没有错误处理
    	printf("task returned:%d
    ",*retval);
    	return 0;
    }
    
    //使用pthread_cancel()取消指定的线程
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    void* task(void* pv){
    	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);		//设置该线程不可取消
    	while(1){
    		printf("I am superman
    ");
    		sleep(1);
    	}
    	return NULL;
    }
    void* task2(void* pv){
    	printf("Cancelling thread...
    ");
    	sleep(2);
    	pthread_cancel(*(pthread_t*)pv);
    	printf("cancelling successfully
    ");
    }
    main(){
    	pthread_t thread;
    	pthread_t thread2;
    	pthread_create(&thread,NULL,task,NULL);
    	pthread_create(&thread2,NULL,task2,(void*)&thread);	
    	pthread_join(thread,NULL);
    	pthread_join(thread2,NULL);
    	return 0;
    }
    /*
    I am superman
    I am superman
    Cancelling thread...
    I am superman
    cancelling successfully
    I am superman		//取消失败了, 说明设置禁止取消成功了
    I am superman
    */
    
  • 相关阅读:
    端口映射到公网工具
    C# app.config文件配置和修改
    C#基本知识点-Readonly和Const的区别
    C#知识点-StopWatch-计时
    python with open as f写中文乱码
    程序员不要去这样的公司
    关于老系统的重构和优化选择
    .Net Remoting 调用远程对象
    自定义的Config节点及使用
    前台线程和后台线程区别与使用技巧
  • 原文地址:https://www.cnblogs.com/xiaojiang1025/p/5950464.html
Copyright © 2020-2023  润新知