首页 > 其他分享 >多线程

多线程

时间:2023-06-16 20:47:42浏览次数:27  
标签:include void 互斥 mutex pthread 线程 多线程

多线程

线程介绍

每个进程都会有一个主线程,在创建进程时创建,往后创建的线程都属于子线程;线程在进程里不断抢占运行时间片;当进程遇到return 结束,所有的线程全部结束。

线程分类

线程主要分为用户级线程和内核级线程

用户级线程主要解决上下文切换问题,其调度由用户控制

内核级线程由内核调度机制实现

当CPU分配给线程的时间片用完后,线程还未执行完毕,线程会从运行态转为阻塞态,将CPU让给其他线程使用。

线程的一些实现

在Linux,线程使用pthread线程库,编译时要加上 -lpthread

每个线程都有自己的线程标识id,pthread_t是线程的数据类型

线程创建

关于线程属性,一般在网络编程上,设置为分离属性,结束后线程自动释放。

!在64位操作系统上,创建线程,当线程进入执行函数后,传递的参数void* arg中,大小是8字节,与4字节int不匹配

线程终止

pthread_exit((void*)0);线程自动退出,同时返回0

在线程执行函数中使用return,也是线程自动退出

pthread_cancel(tid),tid线程被动终止

pthread_join(th,void**return);//等待th线程退出,同时接收其返回值。同时该函数是阻塞的,会一直等待,直到线程结束释放

pthread_join获得pthread_exit()返回的参数

二级指针把获得的值只进行一次解引用赋值,最后,result变量存的值就是线程返回的值

线程清理函数

线程与进程

线程状态

线程属性

线程属性要初始化,用完后要删除

设置线程属性及获取线程属性
#include<pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t* attr,int * detachstate);//获取当前线程属性的分离值
int pthread_attr_setdetachstate(const pthread_attr_t* attr,int detachstate);
成功返回0,出错返回出错错误编号
detachstate取值:
PTHREAD_CREATE_JOINABLE(默认)   正常启动线程
PTHREAD_CREATE_DETACHED        以分离状态启动线程

以分离状态启动的线程,不需要pthread_join()阻塞等待子线程结束释放资源,子线程结束后会自动释放资源

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
void outstate(pthread_attr_t *attr)//获取线程属性的分离值
{
	int state;//存储分离值
	if(pthread_attr_getdetachstate(attr,&state)!=0)//通过函数获取
	{
		perror("getdetachstat error");
	}else
	{
		if(state==PTHREAD_CREATE_JOINABLE) puts("默认");
		else if(state==PTHREAD_CREATE_DETACHED) puts("分离");
		else puts("获取失败");
	}
}
void* th_fun(void* arg)//线程执行函数
{
	long sum=0;
	for(int i=0;i<100;i++)
		sum+=i;
	return (void*)sum;
}
int main()
{
	pthread_t default_th,detach_th;//定义线程
	
	pthread_attr_t attr;//声明线程属性

	pthread_attr_init(&attr);//初始化线程属性
	outstate(&attr);//获取线程分离值

	int err;
	//以线程属性默认值,正常启动线程
	if((err=pthread_create(&default_th,&attr,th_fun,(void*)0))!=0)
	{
		perror("启动失败");
	}
	int res;
	if((err=pthread_join(default_th,(void*)&res))!=0)
	{
		perror("阻塞失败");
	}else
	{
		printf("res%d\n",(int)res);
	}
	puts("-------------------------------------------------------------");
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置线程属性的分离状态为分离
	outstate(&attr);
	if((err=pthread_create(&detach_th,&attr,th_fun,(void*)0))!=0)
	{
		perror("启动失败");
	}
	if((err=pthread_join(detach_th,(void*)&res))!=0)
	{
		perror("阻塞失败");
	}else
	{
		printf("res%d\n",(int)res);
	}
	puts("-------------------------------------------------------------");
	pthread_attr_destroy(&attr);//最后要销毁线程属性
	printf("%lx\n",pthread_self());
	return 0;
}

线程互斥和同步

线程同步主要有同步变量和线程信号量

线程互斥主要有互斥锁,读写锁,线程信号量

assert(int experence) 如果括号里为true则不执行任何;如果括号里为false则显示信息并终止程序
线程互斥
互斥锁

主要通过互斥锁实现pthread_mutex_t

互斥锁属性

设置互斥锁类型的实例:

互斥锁创建的一些步骤

互斥锁:
定义互斥锁的属性
初始化互斥锁属性
可以更改互斥锁的某些属性
定义互斥锁
初始化互斥锁,函数参数传入互斥锁,互斥锁的属性结构体
加锁、解锁
删除互斥锁的属性结构体
删除互斥锁

读写锁

对于互斥锁,对于临界资源的访问,都要加锁,缺乏读并发性,对于一些不会修改临界资源的线程,可以不用加互斥锁,考虑加读锁,多个线程对资源的访问加读锁不互斥

pthread_rwlock_t  都是读锁,不会发生互斥
读、读 不阻塞
读、写 阻塞
写、读 阻塞
写、写 阻塞

线程同步——条件变量

对于互斥锁,只要两种状态:锁定和非锁定

线程进入条件变量等待,要加互斥锁,条件变量是共享资源

条件变量的pthread_cond_wait()函数

条件满足后通知

线程同步的例子
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<time.h>
typedef struct//两个线程共享的结构体资源
{
	int res;
	int is_wait;//用户给出用于判断的条件,判断线程是否去占用资源
	pthread_cond_t cond;//条件变量
	pthread_mutex_t mutex;//互斥锁,限制条件变量
}Result;
void* set_fn(void* arg)//计算
{
	int i=1,sum=0;
	for(;i<=100;i++)
		sum+=i;//将计算结果放到共享结构体
	Result* r=(Result*)arg;
	r->res=sum;

	pthread_mutex_lock(&r->mutex);//对两个线程共享的判断条件加锁,每次只能一个线程访问
	while(!r->is_wait)//得到结果的线程没准备好
	{
		pthread_mutex_unlock(&r->mutex);//释放让另一线程准备
		usleep(100);
		pthread_mutex_lock(&r->mutex);//加锁判断
	}
	pthread_mutex_unlock(&r->mutex);

	//已经准备好了
	//唤醒等待结果的线程
	pthread_cond_broadcast(&r->cond);
	return (void*)0;
}
void* get_fn(void* arg)//获取结果
{
	Result* r=(Result*)arg;
	pthread_mutex_lock(&r->mutex);//加锁修改准备
	r->is_wait=1;//已准备好,进入等待队列

	//准备好后,就进入等待,然后就释放锁,让set_fn函数能获得锁,接着又加锁,进入队列,释放锁,wait等待阻塞
	pthread_cond_wait(&r->cond,&r->mutex);

	pthread_mutex_unlock(&r->mutex);
	int res=r->res;
	printf("结果%d\n",res);
	return (void*)0;
}
int main()
{
	int err;
	pthread_t cal,get;

	Result r;//定义共享资源
	r.is_wait=0;
	pthread_cond_init(&r.cond,NULL);//初始化共享资源里的锁,条件变量
	pthread_mutex_init(&r.mutex,NULL);

	if((err=pthread_create(&cal,NULL,set_fn,(void*)&r))!=0)//启动计算线程,进入set_fn函数,参数为共享结构体
	{
		perror("pthread set error");
	}
	if((err=pthread_create(&get,NULL,get_fn,(void*)&r))!=0)//启动结果线程,进入get_fn函数,参数为共享结构体
	{
		perror("pthread get error");
	}

	pthread_join(cal,NULL);//阻塞
	pthread_join(get,NULL);

	pthread_cond_destroy(&r.cond);//销毁
	pthread_mutex_destroy(&r.mutex);
	return 0;
}
读者写者(写者写后通知读者,读者读后通知写者)
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
typedef struct//共享资源
{
	int value;
	pthread_cond_t rc;//读者条件变量
	pthread_mutex_t rm;//读者锁
	int r_wait;//判断读者是否去占用共享资源读

	pthread_cond_t wc;//写者条件变量
	pthread_mutex_t wm;//写者锁
	int w_wait;//判断写者是否占用共享资源去写
}storage;
void set_data(storage* s,int value)
{
	s->value=value;
}
int get_data(storage*s)
{
	return s->value;
}
void* set_fn(void* arg)//写者:判断读者是否准备好读,唤醒,读完后写者准备好写,等待读者读完唤醒写
{
	storage* s=(storage*)arg;
    int i=1;
	for(;i<=10;i++)
	{
		set_data(s,i);//写入
		printf("0x%lx write data:%d\n",pthread_self(),i);
		pthread_mutex_lock(&s->rm);//加读者锁判断读者是否准备好读
		while(!s->r_wait)
		{
			pthread_mutex_unlock(&s->rm);
			usleep(100);
			pthread_mutex_lock(&s->rm);
		}
		s->r_wait=0;//读者读后置为0
        pthread_mutex_unlock(&s->rm);
		//唤醒读者读,读者在写者等待队列里
		pthread_cond_broadcast(&s->wc);

		//加锁准备好写
		pthread_mutex_lock(&s->wm);
		s->w_wait=1;
		pthread_cond_wait(&s->rc,&s->wm);//等待读者唤醒写,写者进入读者等待队列
		//释放写者锁?
		pthread_mutex_unlock(&s->wm);
	}
	return (void*)0;
}
void* get_fn(void* arg)//读者:准备好读,wait等待写者唤醒读,判断写者是否准备好写,唤醒
{
	storage* s=(storage*)arg;
	int i=1;
	for(;i<=10;i++)
	{
		pthread_mutex_lock(&s->rm);//加读者锁,准备
		s->r_wait=1;
		pthread_cond_wait(&s->wc,&s->rm);//读者等待,放到写者等待队列
		pthread_mutex_unlock(&s->rm);
		printf("0x%lx readyer data:%d\n",pthread_self(),get_data(s));//读取数据

		pthread_mutex_lock(&s->wm);//加写者锁判断写者是否准备好写
		while(!s->w_wait)
		{
			pthread_mutex_unlock(&s->wm);
			usleep(100);
			pthread_mutex_lock(&s->wm);
		}
		s->w_wait=0;//写者写后置为0
        pthread_mutex_unlock(&s->wm);
		//唤醒写者写,写者在读者的等待队列,等待读者读完唤醒
		pthread_cond_broadcast(&s->rc);
	}
	return (void*)0;
}
int main()
{
	int err;
	pthread_t rth,wth;
	storage s;//定义共享资源
	//初始化条件变量,锁
	pthread_cond_init(&s.rc,NULL);
	pthread_cond_init(&s.wc,NULL);
	pthread_mutex_init(&s.rm,NULL);
	pthread_mutex_init(&s.wm,NULL);
	s.r_wait=0;
	s.w_wait=0;

	if((err=pthread_create(&rth,NULL,set_fn,(void*)&s))!=0)//读者线程
		perror("ready thread faile");
	if((err=pthread_create(&wth,NULL,get_fn,(void*)&s))!=0)//写者线程
		perror("writh thread faile");

	pthread_join(rth,NULL);//阻塞
	pthread_join(wth,NULL);
	pthread_cond_destroy(&s.rc);//销毁
	pthread_cond_destroy(&s.wc);
	pthread_mutex_destroy(&s.rm);
	pthread_mutex_destroy(&s.wm);
	return 0;
}

线程信号量

信号量的一些操作:c先运行,c运行后释放sem2,让b运行,b运行释放sem1,让a运行

#include<stdio.h>
#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<time.h>
//定义线程信号量
sem_t sem1;
sem_t sem2;

void* a_fn(void* arg)//线程执行函数
{
	sem_wait(&sem1);//调用阻塞,等待释放
	puts("thread_a sem1 running");
	return (void*)0;
}
void* b_fn(void* arg)
{
	sem_wait(&sem2);//同样调用阻塞,等待阻塞
	puts("thread_b sem2 running");
	sem_post(&sem1);//sem1++;让sem1=0继续运行
	return (void*)0;
}
void* c_fn(void* arg)
{
	puts("thread_c running");
        sem_post(&sem2);//释放sem2信号量,sem2++,让b线程运行
	return (void*)0;
}
int main()
{
	pthread_t a,b,c;
	sem_init(&sem1,0,0);//信号量初始化
	sem_init(&sem2,0,0);

	pthread_create(&a,NULL,a_fn,(void*)0);//启动线程
	pthread_create(&b,NULL,b_fn,(void*)0);
	pthread_create(&c,NULL,c_fn,(void*)0);


	pthread_join(a,NULL);//阻塞
	pthread_join(b,NULL);
        pthread_join(c,NULL);

	sem_destroy(&sem1);//销毁
	sem_destroy(&sem2);
	return 0;
}

信号量初值为1,一些合理pv操作可使信号量实现互斥功能

信号量初值为0,一些合理pv操作可使信号量实现同步功能

死锁

死锁主要是两个线程试图同时占用两个资源,并按不同次序锁定相应共享资源

解决:按相同次序锁定相应共享资源;用pthread_mutex_trylock()加锁,是pthread_mutex_lock的非阻塞版

线程信号

sigset_t set;//信号屏蔽
sigemptyset(&set);//清空
sigaddset(&set,SIGALRM);
pthread_sigmask(SIG_SETMASK,&set,NULL);//对主控线程屏蔽SIGALRM信号
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<time.h>
void sig_handler(int sign)
{
	printf("pthread signal id %lx\n",pthread_self());
	if(sign==SIGALRM)
		puts("timeout...");
	alarm(3);
}
void* th_fn(void* arg)
{
	if(signal(SIGALRM,sig_handler)==SIG_ERR)
	{
		perror("signal sigalrm error");
	}
	//在子线程中设置定时器
	alarm(3);
	int i=1;
	for(;i<=30;i++)
	{
		printf("pthread %lx i:%d\n",pthread_self(),i);
		sleep(1);
	}
	return (void*)0;
}
void* th_fn2(void* arg)//子线程2运行函数
{
	pthread_t th1=(pthread_t)arg;//强转,获得子线程1
	int i;
	for(i=1;i<=10;i++)
	{
		if(i==5)
		{
			pthread_cancel(th1);//在子线程2中通过子线程1,终止子线程1运行
			alarm(0);//取消定时器
		}
		printf("pthread2 cancel:%lx i:%d\n",pthread_self(),i);
		sleep(1);
	}
}
int main()
{
	pthread_t th,th2;
	pthread_attr_t attr;//定义线程属性
	pthread_attr_init(&attr);//初始化线程属性
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//以分离状态启动线程
	int err;
	if((err=pthread_create(&th,&attr,th_fn,(void*)0))!=0)
		perror("thread create error");
	
	if((err=pthread_create(&th2,&attr,th_fn2,(void*)th))!=0)//用子线程2终止子线程1,传入的是子线程1的数据th
		perror("thread create error");
	
	sigset_t set;//信号屏蔽
	sigemptyset(&set);//清空
	sigaddset(&set,SIGALRM);
	pthread_sigmask(SIG_SETMASK,&set,NULL);//对主控线程屏蔽SIGALRM信号

	while(1)
	{
		printf("control pthread %lx running..\n",pthread_self());
		sleep(10);
	}
	puts("control pthread over");
	return 0;
}

gcc编译

链接头文件的编译

标签:include,void,互斥,mutex,pthread,线程,多线程
From: https://www.cnblogs.com/persistencejunjie/p/17486439.html

相关文章

  • 多线程
    1.进程和线程的定义进程:引用程序的执行实例(一个应用对应一个进程)线程:CPU调用和分派的基本单元,进程中执行运算的最小单位2.创建线程的种类继承java.lang.Thread类实现java.lang.Runnable接口3.继承java.lang.Thread类(1)定义MyThread类继承Thread类(2)重写run()方法,编写线程......
  • web worker进程和线程的区别,Chrome 中有哪些常⻅进程,如果我有⼀个耗时很⻓的同步计算
    进程(Process)和线程(Thread)都是操作系统中用于多任务处理的概念。简单地说,一个进程就是一个程序的执行空间,而一个线程则是在执行空间内独立运行的执行路径。区别:进程是系统分配资源的最小单位,线程是操作系统调度的最小单位。各个进程之间是独立的,各个线程之间共享一些资源。创......
  • c++多线程 std::async std::future
    c++标准库中对线程操作有完善的封装,其中最常用到的如std::thread,std::async。EffectiveModernCpp中指出,应尽量使用std::async即基于任务的编程而非基于线程的编程。std::thread在前面的文章有提到过,此处仅对std::async作以记录。正如前面所说,std::async是基于任务的策略,本人理......
  • Java 多线程同步问题的探究(三、Lock来了,大家都让开【1. 认识重入锁】)
    在上一节中,我们已经了解了Java多线程编程中常用的关键字synchronized,以及与之相关的对象锁机制。这一节中,让我们一起来认识JDK5中新引入的并发框架中的锁机制。我想很多购买了《Java程序员面试宝典》之类图书的朋友一定对下面这个面试题感到非常熟悉:问:请对比synchronized......
  • 多线程
    概念程序:一段静态的代码进程:运行中的程序进程作为资源分配的单位线程:进程进一步细化为线程,是一个程序内部的一条执行路径实现方式继承thread创建一个继承Thread的子类子类中重写父类的run()方法创建子类的对象通过子类对象.start()启动线程实现runable接口创建......
  • Java 多线程同步问题的探究(二、给我一把锁,我能创造一个规矩)
    在上一篇中,我们讲到了多线程是如何处理共享资源的,以及保证他们对资源进行互斥访问所依赖的重要机制:对象锁。本篇中,我们来看一看传统的同步实现方式以及这背后的原理。很多人都知道,在Java多线程编程中,有一个重要的关键字,synchronized。但是很多人看到这个东西会感到困惑:“都说同......
  • Python教程-多线程与多进程
    什么是线程,什么是进程?进程是程序(软件,应用)的一个执行实例,每个运行中的程序,可以同时创建多个进程,但至少要有一个。每个进程都提供执行程序所需的所有资源,都有一个虚拟的地址空间、可执行的代码、操作系统的接口、安全的上下文(记录启动该进程的用户和权限等等)、唯一的进程ID、环境变......
  • Linux C 编程——多线程
    线程是计算机中独立运行的最小单位,运行时占用很少的系统资源。与多进程相比,多进程具有多进程不具备的一些优点,其最重要的是:对于多线程来说,其能够比多进程更加节省资源。1、线程创建在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原......
  • v831-openwrt-c-多线程、队列篇
    前言这几天都在搞多线程和队列,但是最后发现由于v831的单核,用了多线程和队列还不如不用,并且吐槽一下c的线程和队列库,特别队列库很难用。线程库#include<pthread.h>      //系统的多线程文件使用条例:使用的很简单,网上的说明很清楚,不需要详细说明指向感悟很鸡肋......
  • Java多线程与静态方法
    Java多线程与静态方法在多线程中使用静态方法会发生什么事?也就是说多线程访问同一个类的static静态方法会发生什么事?是否会发生线程安全问题? publicclassTest{publicstaticvoidoperation(){//...dosomething}} 事实证明只要在静态函数中没有处理多......