Linux应用程序开(day17) ── 线程

内容

介绍

进程:是系统资源分配和调度的基本单位,是线程的容器。是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动。
线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程中可以有多个线程,不同的线程执行不同的任务。

每个进程都拥有自己的数据段、代码段和堆栈段,这就造成进程在进行创建、切换、撤销操作时,需要较大的系统开销。线程自己一般不拥有资源(除了必不可少的程序计数器,一组寄存器和栈),但它可以去访问其所属进程的资源,如进程代码段,数据段以及系统资源(已打开的文件,I/O设备等)

不仅进程间可以并发执行,而且在一个进程中的多个线程之间也可以并发执行。

基本操作

和进程有进程号一样,线程也有相应的线程号,进程号用pid_t类型表示,线程号用pthread_t表示。这里要注意的是pthread_t有的操作系统是按照整型来处理,有的操作系统是按照结构体来处理。
注意:使用GCC编译的时候要加上库的路径 -lpthread

创建线程

#include <pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void*),void * arg);

功能:创建一个线程
参数:
thread:线程标识符的地址
attr:线程属性结构地址
start_routine:线程函数的入口地址。
arg:传给新线程执行函数的参数。(若参数少,直接传过去;若参数多,则使用数组结构体来传递)。‘
返回值:成功返回0,失败返回-1。
实例:
这里我们讨论一下test部分,(void*)test:将整型转换成指针型 ,转换后的666成为0X186,在fun1函数中再将此转换成666。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <pthread.h>

void *fun1(void *arg)
{

	int rec = 0;
	rec = (int)arg;
	printf("线程1.rec:%d \n",rec);
}
void *fun2(void *arg)
{
	int rec = 0;
	rec = *((int *)arg);
	printf("线程2。rec:%d \n",rec);

}

void main()
{
	
	pthread_t tid1,tid2;
	int test = 666;

	pthread_create(&tid1,NULL,fun1,(void *)test);
	pthread_create(&tid2,NULL,fun2,(void *)(&test));

	while(1);
}

等待子线程结束

#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);

功能:等待子线程结束,并回收子线程资源,阻塞在此处。
参数:thread:被等待的线程号。
value_ptr:指针的地址,调用此函数后,指针指向一个存储线程完整退出状态的静态区域,可用来存储被等待线程的返回值(pthread_exit的返回值)
返回值:成功返回0,失败返回非0。
例程:

void *thread(void *arg)
{
	static int test = 100;
	printf("线程开始延迟\n");
	sleep(2);
	printf("线程延迟结束\n");
	pthread_exit((void *)(&test));
}

void main(){

	void *ptr;
	pthread_t tid;

	pthread_create(&tid,NULL,thread,NULL);
	pthread_join(tid,&ptr);
	printf("子线程返回的数据:%d \n",*((int *)ptr));


}

线程分离函数

#include <pthread.h>
int pthread_detach(pthread_t thread);

功能:使线程和进程分离, 成为一个独立的线程,线程终止时,系统会自动回收他的资源。分离出去的线程再进程结束后该线程也结束。分离出去的线程将不被pthread_join函数等待。
参数:thread:将要分离的线程ID
返回值:成功返回0,失败返回非0。
例程

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

void *thread(void *arg)
{
	int i;
	for(i=0; i<5; i++)
	{
		printf("I am runing\n");
		sleep(1);
	}
	return NULL;
}

int main(int argc, char *argv[])
{
	int ret = 0;
	pthread_t tid1;

	ret = pthread_create(&tid1, NULL, thread, NULL);
	if(ret != 0)
	{
		perror("pthread_create");
	}
	pthread_detach(tid1);
	pthread_join(tid1, NULL);
	printf("after jion\n");
	sleep(3);
	printf("I am leaving\n");
	return 0;
}

停止进程

线程正常执行完毕(正常死亡)
线程调用pthread_exit()退出(自杀)
#include <pthread.h>
void pthread_exit(void *value_ptr);

功能:使当前线程退出
参数:value_ptr:线程退出后返回的静态区域,再别的线程中可用pthread_join()来访问。
例程1

void *fun1(void *arg)
{
        static int i = 10;
        printf("地址:%p \n",&i);
        pthread_exit((void*) (&i));
}

void main(){
        void *ptr;
        pthread_t tid;
        pthread_create(&tid,NULL,fun1,NULL);
        pthread_join(tid,&ptr);
        printf("子进程返回:%p , %d \n",ptr,*((int *)ptr));

}

例程2

void *fun1(void *arg)
{
        static int i = 10;
        printf("地址:%p \n",&i);
        pthread_exit((void*)i);
}

void main(){
        void *ptr;
        pthread_t tid;
        pthread_create(&tid,NULL,fun1,NULL);
        pthread_join(tid,&ptr);
        printf("子进程返回:%d \n",(int)ptr);

}
被其他进程取消(他 杀)
#include <pthread.h>
int pthread_cancel(pthread_t thread);

功能:取消某一个线程
参数:thread:要取消的线程的id
返回值:成功返回0,数百返回出错编号
注意:这个函数只是给线程发送取消信号,不意味着取消一定会成功。能否被取消成功取决于一下三点;
1、线程的取消状态(默认是可以取消的)
2、线程取消点
3、线程的取消类型(立即取消或者等到取消点取消)

设置取消状态
pthread_setcancelstate(int state,int *old_state);

功能:设置线程是否可被取消
参数:
state:
PTHREAD_CANCEL_DISABLE:不可以被取消、
PTHREAD_CANCEL_ENABLE:可以被取消。
old_state:保存线程的原来可取消状态的内存地址(NULL为不保存)
例程:

void * fun(void *arg)
{
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
	while(1)
	{
		printf("线程\n");
		sleep(1);
	}
}

void main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,fun,NULL);
	sleep(3);
	pthread_cancel(tid);
	printf("发送退出信号\n");
	pthread_join(tid,NULL);
	printf("主进程退出\n");
}
设置取消点

线程被取消的时候不是马上被取消的,需要执行到取消点才能被中止。

void pthread_testcancel(void);

功能:设置取消点

设置取消类型
pthread_setcanceltype(int type,int *oldtype);

功能:设置取消类型
参数:
type:
PTHREAD_CANCEL_ASYNCHRONOUS:立即取消、
PTHREAD_CANCEL_DEFERRED:不立即被取消
oldtype:
保存调用线程原来的可取消类型的内存地址(可以设置为NULL)

线程清理处理函数

线程可以注册线程退出时要调用的函数,把线程处理函数压栈,所以调用的顺序的注册的顺序相反。

注册线程清理函数
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void*), void *arg);

功能:注册清理函数
参数:
routine:线程清理函数的指针。
arg:传给线程清理函数的参数。

删除线程清理函数
#include <pthread.h>
void pthread_cleanup_pop(int execute);

功能:删除清理函数
参数:
execute:线程清理函数执行标志位。
非0,弹出清理函数,执行清理函数。
0,弹出清理函数,不执行清理函数

注意:pthread_cleanup_pop、pthread_cleanup_push 必须配对使用。 例程:

void cleanup(void *arg)
{
	printf("clean up ptr = %s\n", (char *)arg);
	free((char *)arg);
}

void *thread(void *arg)
{
	char *ptr = NULL;

	printf("this is new thread\n");
	ptr = (char *)malloc(100);

	pthread_cleanup_push(cleanup, (void *)(ptr));

	bzero(ptr, 100);
	strcpy(ptr, "memory from malloc");

	printf("before exit\n");
	pthread_exit(NULL);
	sleep(3);

	printf("before pop\n");
	pthread_cleanup_pop(0);
	// pthread_cleanup_pop(1);
	return NULL;
}

int main(int argc, char *argv[])
{
	int ret = 0;
	pthread_t tid1;

	ret = pthread_create(&tid1, NULL, thread, NULL);
	if(ret != 0)
	{
		perror("pthread_create");
	}
	pthread_join(tid1, NULL);
	printf("process is dying\n");
	return 0;
}

同步与互斥

同步:
两个或两个以上的线程在运行过程中协同步调,按预定的先后次序运行。
互斥:
一个公共资源同一时刻只能被一个线程使用,多个线程不能同时使用公共资源。

互斥锁

互斥锁的初始化

首先要分配互斥锁:
静态分配的互斥锁:
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
动态分配互斥锁:
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
当使用此互斥锁的所有线程都不再使用该互斥锁的时候,需要将此互斥锁销毁

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr)

功能:初始化互斥锁
参数;
mutex:互斥锁地址
attr:互斥锁的属性,NULL为默认属性
返回值:成功返回0,失败返回非0

互斥锁的上锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:对互斥锁进行上锁
参数:mutex:互斥锁地址
返回值:成功返回0,失败返回非0

#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);

功能:对互斥锁上锁,若已经上锁则上锁失败,函数立即返回。
参数:mutex:互斥锁的地址
返回值:成功返回0,失败返回非0

互斥锁的解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t * mutex);

功能:对互斥锁进行解锁
参数:mutex:互斥锁地址
返回值:成功返回0,失败返回非0

互斥锁的销毁
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:对互斥锁进行销毁
参数:mutex:互斥锁地址
返回值:成功返回0,失败返回非0
示例:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void printer(char *str,char* str1){

	printf("加锁之前:%s\n",str1);
	pthread_mutex_lock( &mutex );
	printf("加锁之后:%s\n",str1);
	while(*str != '\0')
	{
		putchar(*str);
		fflush(stdout);
		str++;
		sleep(1);
	}
	printf("\n");
	printf("解锁之前:%s\n",str1);
	pthread_mutex_unlock( &mutex );
	printf("解锁之后:%s\n",str1);
}

void *fun1(void * arg)
{
	char* str = "hello";
	char* str1 = "hello";
	printer(str,str1);
}
void *fun2(void *arg)
{
	char* str = "world";
	char* str1 = "world";
	printer(str,str1);

}


void main(){
	pthread_t tid1,tid2;
	pthread_mutex_init(&mutex,NULL);

	pthread_create(&tid1,NULL,fun1,NULL);
	pthread_create(&tid2,NULL,fun2,NULL);

	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	printf("主进程退出\n");
}

信号量

信号量本质上是一个非负的整数计数器,用来控制对公共资源的访问。当信号量的值大于0时可以访问,否则将阻塞。

信号量的创建
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared,unsigned int value);

功能:创建一个信号量并且初始化信号量的值
参数:
sem:信号量地址。
pshared:决定信号量能否在几个进程间共享,目前Linux没有实现进程间共享信号量,故此值只能为0。
value:信号量的初始值。
返回值;成功返回0,失败返回-1

P操作
#include <semaphore.h>
int sem_wait(sem_t *sem);

功能:将信号量的值减一,若减一之后的值小于0则阻塞
参数:sem:信号量地址
返回值:成功返回0失败返回-1

#include <semaphore.h>
int sem_trywait(sem_t *sem);

功能:将信号量减一,若减一之后的值小于0,则操作失败,立即返回
参数;sem:信号量地址
返回值:成功返回0,失败返回-1

V操作
#include <semaphore.h>
int sem_post(sem_t *sem);

功能:将信号量的值加一,并且唤醒正在等待的线程
参数:sem:信号量地址
返回值:成功返回0,失败返回-1

获取信号量的值
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);

功能:获取某I个信号量的值,并且保存在sval之中
参数:sem:信号量地址
sval:保存获取到的信号量的地址
返回值:成功返回0,失败返回-1

删除信号量
#include <semaphore.h>
int sem_destroy(sem_t *sem);

功能;删除信号量
参数:sem:信号量地址
返回值:成功返回0,失败返回-1
实例:

sem_t sem;
int tmp;
void printer(char *str, char *str1)
{
	sem_getvalue(&sem,&tmp);
	printf("p操作之前:%s ,信号量:%d\n",str1,tmp);
	sem_wait(&sem);
	sem_getvalue(&sem,&tmp);
	printf("p操作之后:%s ,信号量:%d \n",str1,tmp);
	while(*str)
	{
		putchar(*str);
		fflush(stdout);
		str++;
		sleep(1);
	}
	sem_getvalue(&sem,&tmp);
	printf("v操作之前:%s 信号量:%d \n",str1,tmp);
	sem_post(&sem);
	sem_getvalue(&sem,&tmp);
	printf("v操作之后:%s 信号量:%d\n",str1,tmp);
}

void *thread_fun1(void *arg)
{
	char *str = "hello";
	char *str1 = "hello";
	printer(str,str1);
}

void *thread_fun2(void *arg)
{
	char *str = "world";
	char *str1 = "world";
	printer(str,str1);
}

int main(void)
{
	pthread_t tid1, tid2;

	sem_init(&sem, 0, 1);
	pthread_create(&tid1, NULL, thread_fun1, NULL);
	pthread_create(&tid2, NULL, thread_fun2, NULL);

	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);

	return 0;
}

作业

作业一

在线程函数输出进程号与线程号,在主进程同样输出进程号与线程号。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <pthread.h>
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid)
void * thread(void * arg)
{
        printf("线程中--进程号:%d,线程号:%d \n",getpid(),gettid());

}

void main(){
        pthread_t tid;
        void *ptr;
        pthread_create(&tid,NULL,thread,NULL);
        pthread_join(tid,NULL);
        printf("进程中--进程号:%d,线程号:%d \n",getpid(),gettid());

}

谢谢大家的观看,如有错误请指正,谢谢!CSDN记录成长!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值