Linux多线程

目录

一、Linux多线程概述

什么是线程

线程和进程的关系

为什么要使用多线程

二、线程管理

线程ID

创建线程

终止线程

连接与分离

1、线程分离

2、线程连接

3、示例代码

线程属性

1、属性对象

2、线程状态

3、线程栈

4.示例代码


 


 

一、Linux多线程概述

什么是线程

线程是包含在进程内部的顺序执行流,是进程中的实际运作单位,也是系统能够进行调度的最小单位。一个进程可以并发多线程,每条线程并行执行不同的任务

线程和进程的关系

1、一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个主线程

2、资源分配给进程,同一进程的所有线程共享该进程的所有资源

3、线程作为调度和分配的基本单位,进程作为拥有资源的基本单位

4、进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属属于进程的资源

5、在创建或撤销进程时,由于系统都要为之分配和回收资源,导致系统的开销大于创建或撤销线程时的开销

为什么要使用多线程

多线程程序结构相对以多进程程序结构有以下的优势:

1、方便的通信和数据交换:同一进程下的线程之间共享数据空间

2、更高效的利用CPU:当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘,鼠标,菜单的操作,而使用多线程技术,将耗时长的操作置于一个新的线程可以避免这种情况

二、线程管理

线程ID

pthreads线程有一个pthread_t类型的ID来引用。线程可以通过调用pthread_self()函数获取自己的ID。pthread_self()函数原型如下:

pthread_t pthread_self(void);

由于pthread_t可能是一个结构体,可以用pthread_equal()来比较两个线程ID是否相等,原型如下:

int pthread_equal(pthread)t t1,pthread_t t2);

如果t1等于t2,该函数返回一个非0值;否则返回0

创建线程

1.创建线程:在进程中创建新线程的函数是pthread_create(),原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),void *arg);

线程被创建后将立即运行

2、返回值说明:如果pthread_create()调用成功,函数返回0,否则返回一个非0的错误码。下表列出pthread_create()函数调用时必须检查的错误码

错误码出错说明
EAGAIN系统没有创建线程所需的资源
EINVALattr参数无效
EPERM

调用程序没有适当的权限来设定调度策略或attr指定的参数

3、参数说明

(1)thread:用指向新创建的线程的ID

(2)attr:表示一个封装了线程各种属性的属性对象,如果attr为NULL,新线程就使用默认的属性

(3)start_routine:是线程开始执行的时候调用的函数的名字,start_routine函数有一个有指向void的指针参数,并有pthread_create的第四个参数arg指定值,同时start_routine函数返回一个指向void的指针,这个返回值被pthread_join当做退出状态处理

(4)arg:为参数start_routine指定函数的参数

终止线程

进程终止可以通过直接调用exit()、执行main()中的return、或者通过进程的某个其他线程调用exit()来实现。在以上任何一种情况下,所有的线程都会终止。如果主线程在创建了其他线程后没有任务需要处理,那么他应阻塞等待所有线程都结束为止,或者应该调用pthread_exit(NULL)

调用exit()函数会使整个进程终止,而调用pthread_exit()只会使得调用线程终止,同时在创建线程的顶层执行return线程会隐式地调用pthread_exit()。原型如下:

void pthread_exit(void *retval);

retval是一个void类型的指针,可以将线程的返回值当做pthread_exit()的参数传入,这个值同样被pthread_join()当做退出状态处理。如果进程的最后一个线程调用了pthread_exit(),进程会带着状态返回值0退出


示例代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NUM_PTHREADS

void *PrintHello(void *threadid)
{
    long tid;
    tid = (long)threadid;
    printf("Hello World!It's me,thread #%ld\n",tid);
    pthread_exit(NULL);
}

int main(int argc,char *argv[])
{
    pthread_t threads[NUM_PTHREADS];
    int rc;
    long t;

    for(t=0;t<NUM_PTHREADS;t++)
    {
        printf("In main:creating thread %ld\n",t);
        rc = pthread_create(&threads[t],NULL,PrintHello,(void *)t);
        if(rc)
        {
            printf("error");
            exit(-1);
        }
    }

    printf("In main:exit\n");
    pthread_exit(NULL);
    return 0;
}

连接与分离

线程可以分为分离线程和非分离线程两种:

(1)分离线程是指线程退出时线程将释放它的资源和线程;

(2)非分离线程退出后不会立即释放资源,需要另一个线程为它调用pthread_join函数或者进程退出时才会释放资源

只有非分离线程才是可连接的,而分离线程退出时不会报告线程的退出状态

1、线程分离

pthread_detach()函数可以将非分离线程设置为分离线程,原型如下:

int pthread_detach(pthread_t thread);

参数thread是要分离的线程的ID

线程可以自己设置分离,也可以由其他线程设置分离,以下代码线程可以设置自身分离:

pthread_detach(pthread_self());

成功返回0;失败返回一个非0的错误码,下表为pthread_detach的实现必须检查的错误码

    错误码

出错描述
EINVAL

thread参数所表示的线程是不可分离的线程

ESRCH没找到线程ID为thread的线程

2、线程连接

如果一个线程是非分离线程,那么其他线程可以调用pthread_join()函数对非分离线程进行连接,pthread_join()函数原型如下:

int pthread_join(pthread_t thread, void **retval);

pthread_join()函数将调用线程挂起,知道第一个参数thread指定的目标线程终止运行为止

参数:retaval为指向线程的返回值的指针提供一个位置,这个返回值是目标线程调用pthread_eixt()或者return所提供的值。当目标线程无需返回时可以使用NULL值

成功返回0;失败返回一个非0的错误码,下表为pthread_join的实现必须检查的错误码

3、示例代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NUM_THREADS 5

void *BusyWork(void *threadid)
{
	int i;
	long tid;
	tid = (long)threadid;
	double result=0;

	printf("Thread %ld starting...\n",tid);
	for(int i=0;i<1000000;i++){
		result = result + sin(i) * tan(i);
	}

	printf("Thread %ld done.Result = %e\n",tid,result);
	pthread_exit((void *)threadid);
}

int main(int argc,char *argv[])
{
	pthread_t threads[NUM_THREADS];
	int rc;
	long t;
	void *status;

	for(t=0;t<NUM_THREADS;t++)
	{
		printf("In main:creating thread %ld\n",t);
		rc = pthread_create(&threads[t],NULL,BusyWork,(void *)t);
		if(rc)
		{
			printf("error;return code from pthread_create() is %d\n",rc);
			exit(-1);
		}
	}

	for(t=0;t<NUM_THREADS;t++)
	{
		rc=pthread_join(threads[t],&status);
		if(rc){
			printf("error:return code from pthread_join() is %d\n",rc);
			exit(-1);
		}
		printf("Main:completed join with thread %ld having a status of %ld\n",t,(long)status);
	}

	printf("In main:exit!\n");
	pthread_exit(NULL);
	return 0;
}

线程属性

前面线程创建pthread_create()函数,pthread_create()的第二个参数为pthread_attr_t类型,用于设置线程属性

线程的属性基本包括:栈大小、调度策略和线程状态

1、属性对象

(1)初始化属性对象:pthread_attr_init()函数用于将属性对象使用默认值进行初始化,原型如下:

int pthread_attr_init(pthread_attr_t *attr);

参数是一个指向pthread_attr_t的属性对象的指针。成功返回0;否则返回一个非0的错误码

(2)销毁属性对象:使用pthread_attr_destroy()函数,原型如下:

int pthread_attr_destroy(pthread_attr_t *attr);

参数是一个指向pthread_attr_t的属性对象的指针。成功返回0;否则返回一个非0的错误码

2、线程状态

线程有两种状态,取值可能是:

PTHREAD_CREATE_JOINABLE----------非分离线程

PTHREAD_CREATE_DETACHED---------分离线程

(1)获取线程状态:使用pthread_attr_getdetachstate(),原型如下:

int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);

参数attr是一个指向已初始化的属性对象的指针,detachstate是获取结果值的指针。成功返回0;否则返回一个非0的错误码

(2)设置线程状态:使用pthread_attr_setdetachstate(),原型如下:

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

参数attr是一个指向已初始化的属性对象的指针,detachstate是要设置的值。成功返回0;否则返回一个非0的错误码

3、线程栈

每个线程都有一个独立调用栈,线程的栈大小在线程创建的时候就已经固定下来,Linux系统线程的默认栈大小是8MB,只有主线程的栈大小会在运行过程中自动增长。用户可以通过属性对象来设置和获取栈大小

(1)获取线程栈:pthread_attr_getstacksize(),原型如下:

int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);

参数attr是一个指向已初始化的属性对象的指针,stacksize是获取栈大小的指针。成功返回0;否则返回一个非0的错误码

(2)设置线程栈:pthread_attr_setstacksize(),原型如下:

int pthread_attr_setstacksize(pthread_attr_t *attr, sizeo_t *stacksize);

参数attr是一个指向已初始化的属性对象的指针,stacksize是要设置的栈大小。成功返回0;否则返回一个非0的错误码

4.示例代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>

#define handle_error_en(en,msg)\
	do{errno=en;perror(msg);exit(EXIT_FAILURE);}while(0)

#define handle_error(msg)\
	do{perror(msg);exit(EXIT_FAILURE);}while(0)

struct thread_info{
	pthread_t thread_id;
	int thread_num;
	char *argv_string;
};

static void *thread_start(void *arg)
{
	struct thread_info *tinfo = arg;
	char *uargv,*p;
	printf("Thead %d:top of stack near %p;argv_string = %s\n",tinfo->thread_num,&p,tinfo->argv_string);

	uargv=strdup(tinfo->argv_string);
	if(uargv==NULL)
		handle_error("strdup");

	for(p=uargv;*p!='\0';p++)
		*p=toupper(*p);

	return uargv;
}

int main(int argc,char *argv[])
{
	pthread_attr_t attr;
	int stack_size = -1;
	int opt,num_threads,s,tnum;
	void *res;
	struct thread_info *tinfo;
	
	while((opt=getopt(argc,argv,"s:"))!=-1)
	{
		switch(opt){
			case 's':
				stack_size=strtoul(optarg,NULL,0);
				break;
			default:
				fprintf(stderr,"Usage:%s[-s stack-size]arg...\n",argv[0]);
				exit(EXIT_FAILURE);
		}
	}
	num_threads = argc-optind;

	s=pthread_attr_init(&attr);
	if(s!=0)
		handle_error_en(s,"pthread_attr_init");
	if(stack_size>0){
		s=pthread_attr_setstacksize(&attr,stack_size);
		if(s!=0)
			handle_error_en(s,"pthread_attr_setstacksize");
	}

	tinfo=calloc(num_threads,sizeof(struct thread_info));
	if(tinfo==NULL)
		handle_error("calloc");

	for(tnum=0;tnum<num_threads;tnum++)
	{
		tinfo[tnum].thread_num = tnum+1;
		tinfo[tnum].argv_string = argv[optind+tnum];
		s=pthread_create(&tinfo[tnum].thread_id,&attr,&thread_start,&tinfo[tnum]);
		if(s!=0)
			handle_error_en(s,"pthread_create");
	}

	s=pthread_attr_destroy(&attr);
	if(s!=0)
		handle_error_en(s,"pthread_attr_destroy");

	for(tnum=0;tnum<num_threads;tnum++)
	{
		s=pthread_join(tinfo[tnum].thread_id,&res);
		if(s!=0)
			handle_error_en(s,"pthread_join");
		printf("Joined with thread %d;returned value was %s\n",tinfo[tnum].thread_num,(char *)res);
		free(res);
	}

	free(tinfo);
	exit(EXIT_SUCCESS);

	return 0;
}

以下为示例代码运行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值