linux system pthread


原文链接: linux system pthread
一 什么是线程?
- 进程执行的最小单元是线程,被称为轻量级进程(LightWeight Process,LWP)
- 单线程的进程可以简单认为是只有一个线程的进程
- 一个进程在同一时间可以做一件事,有了多线程后一个进程同一时间做很多件事情
- 无论系统有几个cpu,多线程都可以使进程并发处理多个事务
- 一个线程阻塞并不会影响到另一个线程
- 多线程的进程可以尽可能的利用系统cpu系统
- 有几个线程就有几个栈空间
- 进程内所有信息对于线程都是共享的,包括执行代码.全局变量,堆栈内存以及文件描述符

二 线程的标识
- 线程有自己的ID
- 线程ID 用pthread_t 表示, 不能把pthread_t当整数处理
- 线程可以通过pthread_self()函数获得自身的线程ID

线程调用
1)线程创建
- 在进程中只有一个控制线程
- 程序开始运行的时候每个进程只有一个线程,它是以单线程方式启动
  的,在创建多线程以前,进程的运行方式和传统无区别
- gcc在链接的时候需要增加 -pthread选项
- 创建一个线程调用pthread_created函数
#include <pthread.h>
int pthread_created (pthread_t * thread,const pthread_attr_t *attr,
					void *(*start_routine)(void *),void *arg);
//如果pthread返回成功,由参数thread指向的内存单元被设置为新创建进程的线程
//attr参数用于定制各种不同的线程属性
//新创建的线程从start_rtn函数地址开始执行,该函数只有一个
  void *参数,如果需要向start_rtn函数传递多个参数.就需要把这些参数放到一个结构中
  ,然后把这个结构的地址作为void*传入
//线程创建的时候不能保证哪个先运行
//pthread函数成功返回0
//参数arg用来区别线程,或者生成好多相同的线程


/*创建线程*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>
void *func(void *arg)//线程入口函数
{
	return NULL;
}

int main()//控制线程(主线程),main函数退出,所有线程退出
{
	pthread_t thrd;
	if (pthread_created(&thrd,NULL,func,NULL) != 0)//四个参数两个必填
	{
		printf("error is %s",strerror(errno));
	}
	
	
}


2)线程终止
- 任一线程调用了exit函数,整个进程就会终止
- 如果信号默认动作是终止进程,那么信号发送到该进程,整个进程也会被终止
- 单个线程通过以下三种方式退出:
1, 线程从启动函数中返回,返回值是退出线程码
2, 线程可以被同一进程中的其他线程取消
3, 线程调用pthread_exit(NULL);

void pthread_exit(void *arg)
//arg是个无类型的指针,该指针会被其他线程调用pthread_join捕捉

/*创建多个相同线程*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>

void *func(void *arg)//线程入口函数
{
	int *p = (int *)arg;
    int i = *p;
	free(p);
	
	printf("pthread start %d\n",i);
	while(1)
	{
		printf("pthread %d\n",i);
	}
	printf("pthread end	%d\n",i);
	return NULL;
}
 
 void createthr()
 {
	pthread_t thrd1,thrd2;
	
	int *i1 = malloc(sizeof(int));//暂时不能释放i1,所以在堆上分配内存
	int *i2 = malloc(sizeof(int));//或者static int i2也可以
	*i1 = 1; *i2 = 2;
	if (pthread_created(&thrd1,NULL,func,i1) != 0)//四个参数两个必填
	{
		printf("error is %s",strerror(errno));
	}
	
	if (pthread_created(&thrd2,NULL,func,i2) != 0)
	{
		printf("error is %s",strerror(errno));
	}
	 
 }
int main()//控制线程(主线程),main函数退出,所有线程退出
{
	createthr();
	
	
	while(1)
	{
		sleep(1);
	}
	
	return EXIT_SUCCESS;
}

3)线程捕获
int pthread_join (pthread_t th,void **thr_return)
//pthread_join函数用于挂起当前线程,直至th指点的线程终止为止
//如果一个线程的返回值不是NULL,则保存在thr_return地址中
//一个线程所使用的内存资源在应用pthread_join调用之前不会重新分配,
  所以对于每个线程必须调用一次pthread_join函数
//其他线程不能对同一线程再应用pthread_join函数
	
/*创建多个相同线程,以及返回值的处理*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>

void *test(void *arg)
{
	int *a = malloc(sizeof(int));
	*a = 100;
	pthread_exit((void *)a);//或者return a;
}

void *func(void *arg)//线程入口函数
{
	int *p = (int *)arg;
    int i = *p;
	free(p);
	
	printf("pthread start %d\n",i);
	
	
	printf("pthread %d\n",i);
	
	printf("pthread end	%d\n",i);
	
	if(i == 2)
	{
		test();
	}
	return NULL;
}
 
int main()//控制线程(主线程),main函数退出,所有线程退出
{
	pthread_t thrd1,thrd2;
	
	int *i1 = malloc(sizeof(int));//暂时不能释放i1,所以在堆上分配内存
	int *i2 = malloc(sizeof(int));//或者static int i2也可以
	*i1 = 1; *i2 = 2;
	if (pthread_created(&thrd1,NULL,func,i1) != 0)//四个参数两个必填
	{
		printf("error is %s",strerror(errno));
	}
	
	if (pthread_created(&thrd2,NULL,func,i2) != 0)
	{
		printf("error is %s",strerror(errno));
	}
	
	pthread_join(thrd1,NULL);//主线程挂起,等待thrd1退出
    
	int *p = NULL;
	pthread_join(thrd2,(void **)&p)//第二个参数取得返回值
	printf("p = %d\n",*p);
	
	printf("main end\n");
	return EXIT_SUCCESS;
}

4)线程的被分离状态
int pthread_detach(pthread_t th)
//pthread_detach函数使得线程处于被分离状态
//对于被分离状态的线程,不需要调用pthread_join,如果对其他线程对其调用pthread_join
  会失败,并返回EINVAL
//如果不等待一个线程,同时对线程的返回值不感兴趣,可以设置这个线程为被分离状态,
  让系统在线程退出时候自动回收它所占用的资源
//一个线程不能自己调用pthread_detach改变自己为被分离状态,只能由其他线程调用
  pthread_detach函数
  
/*设置可分离状态*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>

void *func(void *arg)//线程入口函数
{
	printf("pthread start \n");
	printf("pthread end \n");
	pthread_exit(NULL);
	 
}
 
int main()//控制线程(主线程),main函数退出,所有线程退出
{
	pthread_t thrd;
	
	pthread_created(&thrd1,NULL,func,NULL) 
	pthread_detach(thrd);
	sleep(1);
	return EXIT_SUCCESS;
}

/*
一旦线程成为可分离线程之后,就不能再使用pthread_join了
可分离线程的使用场景
1、主线程不需要等待子线程
2、主线程不关心子线程的返回码
*/


5)在另一个进程中终止一个线程
int pthread_cancel(pthread_t th);
//pthread_cansel函数允许一个线程取消th指定的另一个线程
//函数成功,返回0,否则返回非0


/*在另一个进程中终止一个线程代码*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>

void *func1(void *arg)//线程入口函数
{
	printf("pthread start \n");
	while(1)
	{
		printf("pthread living\n");
		sleep(1);
	}
	printf("pthread end \n");
	pthread_exit(NULL);
	 
 }
 
 
void *func2(void *arg)//线程入口函数
{
	sleep(5);
	pthread_t thr;
	thr = *(pthread_t *)arg;
	pthread_cancel(thr);
	return (NULL);
	 
 }
 
int main()//控制线程(主线程),main函数退出,所有线程退出
{
	pthread_t thrd1,thrd2;
	
	pthread_created(&thrd1,NULL,func1,NULL);
    pthread_created(&thrd2,NULL,func2,&thrd1);	
	
	pthread_join(thrd1,NULL);
	pthread_join(thrd2,NULL);
	
	printf("main end\n");

	return EXIT_SUCCESS;
}


6)判断两个线程是否相等*/
int pthread_equal(pthread_t th1,pthread_t th2);
//pthread_equal函数比较th1和th2是否为同一个线程,由于不可以
  将pthread数据类型认为是整数,所以不能用整数的方式去比较
//如果th1与th2相同,函数返回非0值;如果不同,函数返回0


7)线程属性
- 以前调用pthread_created传入的attr参数都是空指针,而不是指向pthread_attr_t结构的指针
- 可以使用pthread_attr_t结构修改线程的默认属性,并把这些属性于创建的线程联系起来
- 可以使用pthread_attr_init函数初始化pthread_attr_t结构
- 调用pthread_attr_init后,pthread_attr_t结构所包含的内容就是操作系统实现支持线程的默认
  值.如果要修改其中个别属性的值,需要调用其他函数

int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_init(pthread_attr_t *attr);
//函数pthread_attr_init初始化attr结构
//函数pthread_attr_destroy释放attr内存空间
//pthread_attr_t的结构对于应用程序来讲是不透明的,应用程序不需要了解有关结构的内部
//可以通过pthread_attr_t属性在创建线程时就指定线程detach,而不用使用pthread_detach函数

int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate)
//函数pthread_attr_setdetachstate把线程属性设置为下面两个合法值之一
		值							说明						
PTHREAD_CREATE_DETACHED		设置线程为分离状态
PTHREAD_CREATE_JOINABLE     设置线程为正常状态
 

/*调整属性示例代码*/

void *func(void *arg)
{
	printf("pthread start\n");
	while(1)
	{
		printf("thread is running\n");
		sleep(1);
	}
	pthread_exit(NULL);
}

int main()//控制线程(主线程),main函数退出,所有线程退出
{
	pthread_t thrd;
	
	pthread_attr_t attr;
	pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	pthread_created(&thrd,&attr,func,NULL);
	pthread_attr_destroy(&attr);
	sleep(2);
	
	printf("main end\n");

	return EXIT_SUCCESS;
}

三 线程同步技术
- 互斥(mutex)是相互排斥的意思,它是一种锁或者信号灯
- 互斥用来保护多个线程共享数据和结构不会被同时修改
  一个互斥锁只能有两个状态:locked和unlocked
- 加锁后互斥不让其他线程访问
- 任何时刻只能有一个线程来掌握某个互斥上锁
- 一个线程如果试图在一个已经加锁的互斥上再加锁,这个线程会挂起,
  知道加锁的线程释放掉互斥锁为止

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//PTHREAD_MUTEX_INITIALIZER 是初始化一个加速锁的宏定义
//pthread_mutex_lock用于给mutex加锁
//pthread_mutex_unlock用于给mutex解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//检测是否锁

//最恰当的互斥用法

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//初始化了一个MUTEX锁

int count = 0;

void *func1(void *arg)
{

	int *a = (int *) arg;
	printf("thread%d start\n", *a);
	int i;
	for (i = 0; i < 10; i++)
	{
		printf("thread%d is running\n", *a);
		sleep(1);
		pthread_mutex_lock(&mutex);//给mutex加锁,这是一条原子操作,不可能出现两个线程同时执行这个代码
		count++;//这段代码受到保护,永远只有一个线程可以操作
		pthread_mutex_unlock(&mutex);//给mutex解锁
	}
	printf("thread%d end\n", *a);

	pthread_exit(NULL);
}

int main(int arg, char * args[])
{
	printf("process start\n");
	pthread_t thr_d1, thr_d2;
	int i[2];
	i[0] = 1;
	i[1] = 2;


	pthread_create(&thr_d1, NULL, func1, &i[0]);
	pthread_create(&thr_d2, NULL, func1, &i[1]);
	pthread_join(thr_d1, NULL);
	pthread_join(thr_d2, NULL);

	printf("process end\n");
	return 0;
}
`