多线程编程

线程是任务调度和执行的基本单位

为什么会有线程?
1、进程实现多任务的缺点
进程间切换的计算机资源开销很大,切换效率非常低
进程间数据共享的开销也很大
2、线程和进程的关系
1)线程是进程的一个执行单元,是进程内的调度实体。
2)同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
3)进程退出,进程中所有线程全部退出;
4)一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮
5)线程不可能完全替代进程
6)线程拥有独立的属性
在这里插入图片描述
线程的特点:
在这里插入图片描述
线程操作
1、线程创建
在这里插入图片描述
2、线程退出
在这里插入图片描述
注意:pthread_cancel 有个bug,必须要发生系统调用
pthread_cancel

   (1)函数原型
        #include <pthread.h>
        
        int pthread_cancel(pthread_t thread);
        
        1)功能
            当次线程是死循环时,可以调动这个函数主动取消该线程。(必须发生系统调用)
           
        2)返回值
             成功返回0,失败返回非零错误号。
          
        2)参数
           thread:要取消线程的TID

void pthread_exit(void *retval);

        1)功能
             线程调用这个函数时,可以主动退出(终止)。
             
             这类似于exit函数,不过exit是终止整个进程的,而pthread_exit是终止次线程的。
             如果你在次线程里面调用错误,调用的是exit,整个进程就终止了。      
           
        2)返回值
             成功返回0,失败返回非零错误号。
             
        3)参数
             retval:线程结束时的返回值。
             
             如果返回值很多时,就封装成一个结构体,返回结构体变量的地址即可。

3、线程等待
在这里插入图片描述
int pthread_join(pthread_t thread, void **retval);

     1 )功能:阻塞等待tid为thread的次线程结束,结束时该函数会回收次线程所占用的所有资源(存储空间)。   这个函数只对次线程有意义,对主线程没有意义,因为主线程结束时真个进程就结束了,整个进程资源
           会由父进程回收。
           
          
                
                这个函数一般都是由主线程调用,以回收相关次线程的资源,当然次线程也是可以调用这个函数来回
             收其它次线程资源的。
        
        
        2)返回值
             成功返回0,失败返回错误号。
        
             
        3)参数
           (a)thread:指定要回收次线程的TID
           (b)retval:次线程函数返回的返回值

请添加图片描述
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);

                       功能
           (a)pthread_cleanup_push
                  将类型为void (*routine)(void *)函数注册为“线程退出处理函数”,arg为传递给退出处理函数的参数。      注册的原理就是将处理函数地址压入线程栈。
                  我们可以反复调用该函数注册多个退出处理函数,但是一般一个就够了。
           
           (b)pthread_cleanup_pop
                  执行这个函数时,
                  · 如果参数写!0:会将压入栈中的推出处理函数地址弹出,然后调用退出函数进行线程的扫尾处理。
                  · 如果参数写0:不弹出调用
                     
                     如果注册了多个线程退出处理函数的话,由于栈先进后出的特点,所以注册压栈的顺序与弹
                  栈调动的顺序刚好相反。
                            
                     这个函数必须和pthread_cleanup_push配对出现,有一个pthread_cleanup_push,就必须要
                  对应有一个pthread_cleanup_pop,就算这个函数调用不到也必须写,否者编译时不通过,
                  这就好比{}是成对出现的,缺一个都会报错。

线程状态
在这里插入图片描述
pthread_detach

   (1)函数原型
        #include <pthread.h>
        
        int pthread_detach(pthread_t thread);
        
        1)功能
             如果次线程的资源不希望别人调用pthread_join函数来回收的话,而是希望自己在结束时,自动回收
           资源的话,就可以调用这个函数。
             
             这个函数的功能就是分离次线程,让次线程在结束时自动回收资源。
           
        2)返回值
             成功返回0,失败返回错误号。
             
        3)参数
             thread:你要分离的那个次线程的TID。

线程同步

在这里插入图片描述

初始化互斥锁的函数

            (a)函数原型
                #include <pthread.h>
                 int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
                                                          const pthread_mutexattr_t *restrict attr);
                
                · 功能:初始化定义的互斥锁
                     什么是初始化,就是设置互斥锁所需要的值。
                            
                · 返回值
                     总是返回0,所以这个函数不需要进行出错处理。
                     
                     
                · 参数
                  - mutex:互斥锁,需要我们自己定义。
                     比如:pthread_mutex_t mutex;
                       
                     pthread_mutex_t是一个结构体类型,所以mutex实际上是一个结构体变量。

加锁解锁

pthread_mutex_lock(&mutex)(阻塞加锁)访问临界区加锁操作
pthread_mutex_trylock( &mutex)(非阻塞加锁); pthread_mutex_lock() 类似,不同的是在锁已经被占据时返回 EBUSY 而不是挂起等待。
pthread_mutex_unlock(&mutex): 访问临界区解锁操
在这里插入图片描述
在这里插入图片描述

初始化信号量的函数

               (a)函数原型
                #include <semaphore.h>   int sem_init(sem_t *sem, int pshared, unsigned int value);
           
                · 功能
                  初始化线程信号量集合中的某个信号量,给它设置一个初始值。
           
                · 返回值
                  成功返回0,失败返回-1,errno被设置。
                  注意信号量的错误号不是返回的,而是设置到errno中。
                
                · 参数
                  - sem:信号量集合中的某个信号量
                  
                          信号量集合需要我们自己定义,
                          
                          比如:sem_t sem[3],
                          线程信号量集合其实就是一个数组,数组每个元素就是一个信号量。
                          
                          sem[0]:第一个信号量
                          sem[1]:第二个信号量
                          sem[2]:第三个信号量
                          
                          sem_init(&sem[0], int pshared, unsigned int value);
                  
                       线程信号量集合其实就是自定义的一个数组,而进程信号量集合则是通过semget函数创建的。
                       
                       我们只要把数组定义为全局变量,所有的线程即可共享使用,不像进程信号量,需要semid
                     才能实现共享操作。
                     
                     
                  - pshared:
                    + 0:给线程使用
                    + !0:可以给进程使用
                        不过对于进程来说,我们更多的还是使用进程信号量,因为线程信号量用到
                      进程上时,存在一些不稳定的情况。
                
                  - value:初始化值
                   对于二值信号量来说,要么是1,要么是0。

P、V操作

                P操作
           (a)函数原型
                #include <semaphore.h>
                int sem_wait(sem_t *sem);//阻塞p操作
        
                
             · 功能:阻塞p操作集合中某个信号量,值-1
                如果能够p操作成功最好,否则就阻塞直到p操作操作成功为止。
                
                
             · 返回值:成功返回0,失败返回-1,errno被设置。
             
             · 参数:p操作的某个信号量。
                比如:sem_wait(&sem[0]);
             
             · sem_wait的兄弟函数
                  
                  int sem_trywait(sem_t *sem):不阻塞
                     如果能够p操作就p操作,如果不能p操作就出错返回,不会阻塞。
                  
                  int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
                     可以设置阻塞时间,如果能够p操作就p操作,不能就阻塞,如果在设置的时间内好没有
                     p操作成功就是出错返回,不再阻塞。
                  
                  这两个函数了解即可,不需要掌握,如果你真的用到了,自己举一反三即可搞定。
                
                  
           (b)代码演示
                  
                  
                  
                  
        4)v操作
           (a)函数原型
                #include <semaphore.h>

                int sem_post(sem_t *sem);
           
                · 功能:对某个信号量进行v操作,v操作不存在阻塞问题。
                     v操作成功后,信号量的值会+1
                
                · 返回值:成功返回0,失败返回-1,errno被设置。
                
           (b)代码演示
                sem_post(&sem[0]);

在这里插入图片描述
在这里插入图片描述

使用条件变量

     2)等待条件的函数
        (a)函数原型
             #include <pthread.h>
             
             int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 
           
             · 功能:
                  检测条件变量cond,如果cond没有被设置,表示条件还不满足,别人还没有对cond进行设置,此时
                pthread_cond_wait会休眠(阻塞),直到别的线程设置cond表示条件准备好后,才会被唤醒。
                
             · 返回值:成功返回0,失败返回非零错误号
             
             · 参数
                - cond:条件变量
                - mutex:和条件变量配合使用的互斥锁    
             
           
        (c)pthread_cond_wait的兄弟函数
             int pthread_cond_timedwait(pthread_cond_t *restrict cond, \
                             pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
                
                  多了第三个参数,用于设置阻塞时间,如果条件不满足时休眠(阻塞),但是不会一直休眠,
                当时间超时后,如果cond还没有被设置,函数不再休眠。
   
                  
                  
      3)设置条件变量的函数   
      
        (a)函数原型
             #include <pthread.h>
             
             int pthread_cond_signal(pthread_cond_t *cond);
             
             
           · 功能
                当线程将某个数据准备好时,就可以调用该函数去设置cond,表示条件准备好了,
             pthread_cond_wait检测到cond被设置后就不再休眠(被唤醒),线程继续运行,使用别的线程
             准备好的数据来做事。
                
                当调用pthread_cond_wait函数等待条件满足的线程只有一个时,就是用pthread_cond_signal
             来唤醒,如果说有好多线程都调用pthread_cond_wait在等待时,使用
                int pthread_cond_broadcast(pthread_cond_t *cond);
                
                它可以将所有调用pthread_cond_wait而休眠的线程都唤醒。
      
      
        (b)代码演示
             
                  
             
             调用pthread_cond_signal去设置条件变量,相当是给pthread_cond_wait发送了一个线程间专
           用的信号,通知调用pthread_cond_wait的线程,某某条件满足了,不要再睡了,赶紧做事吧。

删除条件变量 ,也需要把互斥锁删除

           (a)函数原型
             #include <pthread.h>     
              int pthread_cond_destroy(pthread_cond_t *cond);

代码案例

pthread1.c

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

void *thread1(void *arg)
{
	while(1)
	{
		printf("hello world!\n");
		sleep(1);
	}
}

int main()
{
	pthread_t id;

	if(pthread_create(&id,NULL,thread1,NULL)!=0)
	{
		perror("pthread create error!\n");
		exit(1);
	}
	pause();//挂起进程

	return 0;
}

在这里插入图片描述

pthread2.c

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

#define MAXSIZE 1024

char buffer[MAXSIZE];

void *thread1(void *arg)
{
	while(1)
	{
	//	memset(buffer,0,sizeof(buffer));
		scanf("%s",buffer);
		sleep(2);
	}
}

void *thread2(void *arg)
{
	while(1)
	{
		printf("buffer=%s\n",buffer);
		sleep(2);
	}
}

int main()
{
	pthread_t id1;

	if(pthread_create(&id1,NULL,thread1,NULL)!=0)
	{
		perror("pthread1 create error!");
		exit(1);
	}

	pthread_t id2;

	if(pthread_create(&id2,NULL,thread2,NULL)!=0)
	{
		perror("pthread2 create error!");
		exit(1);
	}

	pause();

	return 0;
}

在这里插入图片描述

pthread3.c

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

#define MAXSIZE 1024

char buffer[MAXSIZE];

void *thread1(void *arg)
{
	int num=*((int *)arg);

	printf("num=%d\n",num);

	while(1)
	{
	//	memset(buffer,0,sizeof(buffer));
		scanf("%s",buffer);
		sleep(2);
	}
}

void *thread2(void *arg)
{
	char *ptr=(char *)arg;

	printf("ptr=%s\n",ptr);

	while(1)
	{
		printf("buffer=%s\n",buffer);
		sleep(2);
	}
}

int main()
{
	pthread_t id1;
	
	int num =5;

	char *ptr ="hello world";

	if(pthread_create(&id1,NULL,thread1,(void *)(&num))!=0)
	{
		perror("pthread1 create error!");
		exit(1);。、
	}

	pthread_t id2;

	if(pthread_create(&id2,NULL,thread2,(void *)ptr)!=0)
	{
		perror("pthread2 create error!");
		exit(1);
	}

	pause();

	return 0;
}

在这里插入图片描述

pthread4.c

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

int i=0;

void *print(void *arg)
{
	while(1)
	{
		i++;
		printf("hello world!\n");
		sleep(2);
	}
}

int main()
{
	pthread_t id;

	pthread_create(&id,NULL,print,NULL);

	sleep(3);

	pthread_cancel(id);

	pthread_join(id,NULL);

	printf("thread is exit!\n");
//	pause();
	
	return 0;
}

在这里插入图片描述

pthread5.c

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

int i=0;
int num =5;

void *print(void *arg)
{
	while(1)
	{
		i++;
		printf("hello world!\n");
		sleep(2);
		pthread_exit((void *)(&num));
	}
}

int main()
{
	pthread_t id;

	pthread_create(&id,NULL,print,NULL);

	sleep(3);

//	pthread_cancel(id);

	void *num;

	pthread_join(id,&num);

	printf("thread is exit=%d\n",*((int*)num));
//	pause();
	
	return 0;
}

在这里插入图片描述

pthread6.c

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

int i=0;
int num =5;

void thread_exit(void *arg)
{
	printf("my exit!\n");
}

void *print(void *arg)
{
	pthread_cleanup_push(thread_exit,NULL);
	while(1)
	{
		i++;
		printf("hh\n");
		sleep(2);
//		pthread_exit((void *)(&num));
		pthread_exit(NULL);

	//	return (void *)0;//不会弹栈
	}
	pthread_cleanup_pop(!0);
}

int main()
{
	pthread_t id;

	pthread_create(&id,NULL,print,NULL);

	pthread_detach(id);

	sleep(3);

//	pthread_cancel(id);

	void *num;

	pthread_join(id,&num);

//	printf("thread is exit=%d\n",*((int*)num));
//	pause();
	
	return 0;
}

请添加图片描述

pthread7.c

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

int count=0;

pthread_mutex_t mutex;
pthread_cond_t cond;
void* add_1(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		count++;
		if(count==5)
		{
			pthread_cond_signal(&cond);
		}
		sleep(1);
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
}

void* print_1(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
	//	printf("%d\n",count);
	//	sleep(1);
	//	pthread_mutex_unlock(&mutex);
	//	sleep(1);
		if(count!=5)
		{
			pthread_cond_wait(&cond,&mutex);
		}
	
		printf("count=%d\n",count);
		count=0;

		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
}	

int main()
{
	pthread_t id1;
	pthread_t id2;

	int ret;

	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);

	ret = pthread_create(&id1,NULL,add_1,NULL);
	if(ret!=0)
	{
		perror("thread create error!");
		exit(1);
	}

	ret =pthread_create(&id2,NULL,print_1,NULL);
	if(ret!=0)
	{		
		perror("thread create error!");
		exit(1);
	}

	pthread_join(id1,NULL);
	pthread_join(id2,NULL);

	return 0;
}

在这里插入图片描述

pthreadfile.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#define MAXSIZE 1024

struct  message
{
	int fd;
	pthread_mutex_t mutex;//尽量写成全局
};

pthread_mutex_t mutex;

char buffer[MAXSIZE];

void *thread1(void *arg)
{
	struct message msg=*((struct message *)arg);
	int fd=msg.fd;

	while(1)
	{
		pthread_mutex_lock(&mutex);
		write(fd,"hello",5);
		write(fd,"world\n",6);
//		sleep(2);
		pthread_mutex_unlock(&mutex);
	}
}

void *thread2(void *arg)
{	
	struct message msg=*((struct message *)arg);

	int fd=msg.fd;

	while(1)
	{
		pthread_mutex_lock(&mutex);
		write(fd,"hhhhh",5);
		write(fd,"wwwww\n",6);
//		sleep(2);
		pthread_mutex_unlock(&mutex);
	}
}

int main()
{
	pthread_t id1;

	struct message msg={.mutex=PTHREAD_MUTEX_INITIALIZER};	
		
	int fd;

	fd=open("./a.txt",O_RDWR|O_CREAT|O_APPEND,0644);
	if(fd<0)
	{
		perror("open file error!");
		exit(1);
	}

	msg.fd=fd;
	pthread_mutex_init(&mutex,NULL);

	if(pthread_create(&id1,NULL,thread1,(void *)(&msg))!=0)
	{
		perror("pthread1 create error!");
		exit(1);
	}

	pthread_t id2;

	if(pthread_create(&id2,NULL,thread2,(void *)(&msg))!=0)
	{
		perror("pthread2 create error!");
		exit(1);
	}

//	pause();
	
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);

	return 0;
}

在这里插入图片描述
在这里插入图片描述

pthreadfile1.c//信号量的使用,结果同上

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<semaphore.h>
#define MAXSIZE 1024

struct  message
{
	int fd;
	//pthread_mutex_t mutex;//尽量写成全局
};

pthread_mutex_t mutex;
sem_t sem[1];

char buffer[MAXSIZE];

void *thread1(void *arg)
{
	struct message msg=*((struct message *)arg);
	int fd=msg.fd;

	while(1)
	{
	//	pthread_mutex_lock(&mutex);
		sem_wait(&sem[0]);
		write(fd,"hello",5);
		write(fd,"world\n",6);
//		sleep(2);
	//	pthread_mutex_unlock(&mutex);
		sem_post(&sem[0]);
	}
}

void *thread2(void *arg)
{	
	struct message msg=*((struct message *)arg);

	int fd=msg.fd;

	while(1)
	{
	//	pthread_mutex_lock(&mutex);
		sem_wait(&sem[0]);
		write(fd,"hhhhh",5);
		write(fd,"wwwww\n",6);
//		sleep(2);
	//	pthread_mutex_unlock(&mutex);
		sem_post(&sem[0]);
	}
}

int main()
{
	pthread_t id1;

	struct message msg={.mutex=PTHREAD_MUTEX_INITIALIZER};	
		
	int fd;

	fd=open("./a.txt",O_RDWR|O_CREAT|O_APPEND,0644);
	if(fd<0)
	{
		perror("open file error!");
		exit(1);
	}

	msg.fd=fd;
	pthread_mutex_init(&mutex,NULL);

	sem_init(&sem[0],0,1);

	if(pthread_create(&id1,NULL,thread1,(void *)(&msg))!=0)
	{
		perror("pthread1 create error!");
		exit(1);
	}

	pthread_t id2;

	if(pthread_create(&id2,NULL,thread2,(void *)(&msg))!=0)
	{
		perror("pthread2 create error!");
		exit(1);
	}

//	pause();
	
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);

	return 0;
}

进程VS线程
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&*Savior

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值