0
点赞
收藏
分享

微信扫一扫

Linux C/C++之线程基础

林肯公园_97cc 2022-04-23 阅读 80

目录

1. 什么是线程

1.1 进程与线程

1.2 线程

1.3 线程的发展史

2. 如何创建,使用线程

2.1 pthread_create函数创建线程

2.2 pthread_create函数的使用

2.3 主线程结束, 分支线程也会随之结束

2.4 同一进程内多个线程共用进程资源

2.5 线程使用进程开辟的动态内存,主线程提前释放内存会造成的问题

2.6 使用地址传递传递数据到线程运行函数

2.7 使用值传递传递数据到线程运行函数

2.8 结构体数据类型的参数传递

3. 线程的结束

3.1 自然结束

3.2 主线程结束, 分支线程随之结束 

3.3 线程自己结束自己

3.4 其它线程结束某个线程

4. 线程的同步

4.1 临界区域与临界数据

4.2 多个线程同时操作临界数据导致的问题

4.3 使用线程同步解决临界数据脏的方式

4.3.1 原子锁(atomic)

4.3.2 自旋锁(spin)

4.3.3 信号量(sem)

4.3.4 读写锁(rwlock)

4.3.5 互斥锁(mutex)

4.3.6 临界变量(cond)


1. 什么是线程

1.1 进程与线程

1.2 线程

1.3 线程的发展史

2. 如何创建,使用线程

2.1 pthread_create函数创建线程

 

创建并执行线程,执行代码为线程函数,并且和当前主线程并行

2.2 pthread_create函数的使用

//使用pthread_create 创建一个线程
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	int m = 1;
	while(1){
		printf("线程 --- m: %d\n",m++);
		sleep(1);
	}
}
int main(){
	int n = 1;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,NULL);
	while(1){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

 

2.3 主线程结束, 分支线程也会随之结束

//当主线程结束,分支线程也会随即结束
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	int m = 1;
	while(1){
		printf("线程 --- m: %d\n",m++);
		sleep(1);
	}
}
int main(){
	int n = 1;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,NULL);
	for(int i = 0; i < 5; i++){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

 

2.4 同一进程内多个线程共用进程资源

//同一进程内的线程共用进程的资源
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

int num = 0;
void* pFunc1(void* arg){
	while(1){
		printf("线程1 --- num: %d\n",num++);
		sleep(1);
	}
}

void* pFunc2(void* arg){	//先于线程一执行
	while(1){
		printf("线程2 --- num: %d\n",num++);
		sleep(1);
	}
}

int main(){
	//创建一个线程
	pthread_t pid1,pid2;
	pthread_create(&pid1,NULL,pFunc1,NULL);
	pthread_create(&pid2,NULL,pFunc2,NULL);
	while(1){
		printf("主函数 --- num: %d\n",num++);
		sleep(1);
	}
	return 0;
}

2.5 线程使用进程开辟的动态内存,主线程提前释放内存会造成的问题

//当使用动态内存申请时,主线程提前将内存释放会造成的问题
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

void* pFunc(void* arg){
	while(1){
		printf("线程 --- arg: %d\n",*(int*)arg);
		sleep(1);
	}
}
int main(){
	int* n = (int*)malloc(4);
	*n = 666;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,n);
	for(int i = 0; i < 5; i++){
		if(i == 2){
			free(n);
			n = NULL;
		}
		printf("主函数 --- n: %d\n",(*n)++);
		sleep(1);
	}
	return 0;
}

2.6 使用地址传递传递数据到线程运行函数

//使用变量的地址传参 &
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	while(1){
		printf("线程 --- arg: %d\n",(*(int*)arg)++);
		sleep(1);
	}
}
int main(){
	int n = 666;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,&n);
	while(1){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

2.7 使用值传递传递数据到线程运行函数

//使用(void*)变量直接传参
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	while(1){
		printf("线程 --- arg: %d\n",(int)arg++);
		sleep(1);
	}
}
int main(){
	int n = 666;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,(void*)n);
	while(1){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

2.8 结构体数据类型的参数传递

//结构体类型的参数传递
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

struct Student{
	char name[20];
	int age;
	double score;
};

void* pFunc(void* arg){
	struct Student* stu = (struct Student*)arg;
	while(1){
		printf("线程===\n");
		printf("name:%s,age:%d,score:%g\n",
			stu->name,stu->age,stu->score);
		sleep(1);
	}
}

int main(){

	struct Student stu = {"张三",18,66.66};
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,&stu);
	for(int i = 0; i < 5; i++){
		printf("主函数---\n");
		sleep(1);
	}

	return 0;
}

3. 线程的结束

3.1 自然结束

3.2 主线程结束, 分支线程随之结束 

注: 主线程最好等待分支线程结束再结束

pthread_join函数使用

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

void* func(void* arg){
	while(1){
		sleep(2);
		printf("线程-----\n");	
	}
}

int main(){
	pthread_t pid;

	pthread_create(&pid,NULL,func,NULL);

	printf("主线程-----\n");

	return 0;
}

没有pthread_join函数的情况 , 主线程不会等待分支线程结束后再结束

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

void* func(void* arg){
	while(1){
		sleep(2);
		printf("线程-----\n");	
	}
}

int main(){
	pthread_t pid;

	pthread_create(&pid,NULL,func,NULL);

	printf("主线程-----\n");

	pthread_join(pid,NULL);

	return 0;
}

 有pthread_join的情况,主线程会等待分支线程结束后,自己再结束

3.3 线程自己结束自己

//线程自己结束自己pthread_exit
//void pthread_exit(void *retval); retval线程结束的返回值
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void* func(void* arg){
	int num = 666;
	for(int i = 0;i < 10; i++){
		if(i == 5){
			//pthread_exit((void*)num);
			pthread_exit((void*)"线程退出!");
		}
		printf("线程 >> %d\n",(*(int*)arg)++);
		sleep(1);
	}
	
}

int main(){
	int n = 1;
	void* pthread_res; //接收线程结束的返回值
	pthread_t pid;

	pthread_create(&pid,NULL,func,&n);

	pthread_join(pid,&pthread_res); //等待线程结束并接收返回值

	//printf("pthread_return: %d\n",(int)pthread_res);
	printf("pthread_return: %s\n",(char*)pthread_res);
	printf(" >> %d\n",n);

	return 0;
}

3.4 其它线程结束某个线程

pthread_cancel函数: 仅向线程发送一个结束请求, 至于是否结束看pthread_setcancelstate()函数参数一是忽略(PTHREAD_CANCEL_DISABLE)还是响应(PTHREAD_CANCEL_ENABLE)   

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

int n = 1;
pthread_t pid1;

void* func1(void* arg){
	while(1){
		printf("线程1 >> %d\n",n++);
		//PTHREAD_CANCEL_ENABLE(缺省:响应)
		//PTHREAD_CANCEL_DISABLE(忽略)
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
		pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
		//PTHREAD_CANCEL_DEFERRED(下个取消点)
		//PTHREAD_CANCEL_ASYNCHRONOUS(异步(随时)取消)

		sleep(1);
	}
}

int main(){

	pthread_create(&pid1,NULL,func1,NULL);
	while(1){
		if(n == 5){
			pthread_cancel(pid1);
			break;
		}
	}

	pthread_join(pid1,NULL);

	printf(">> %d\n",n);

	return 0;
}

 PTHREAD_CANCEL_ENABLE: 

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

int n = 1;
pthread_t pid1;

void* func1(void* arg){
	while(1){
		printf("线程1 >> %d\n",n++);
		//PTHREAD_CANCEL_ENABLE(缺省:响应)
		//PTHREAD_CANCEL_DISABLE(忽略)
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
		pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
		//PTHREAD_CANCEL_DEFERRED(下个取消点)
		//PTHREAD_CANCEL_ASYNCHRONOUS(异步(随时)取消)
		//pthread_testcancel();  //加一个取消点

		printf("sleep1\n");
		sleep(1);

		printf("sleep2\n");
		sleep(1);

		printf("sleep3\n");
		sleep(1);
	}
}

int main(){

	pthread_create(&pid1,NULL,func1,NULL);
	while(1){
		if(n == 5){
			pthread_cancel(pid1);
			break;
		}
	}

	pthread_join(pid1,NULL);

	printf(">> %d\n",n);

	return 0;
}

 

4. 线程的同步

4.1 临界区域与临界数据

4.2 多个线程同时操作临界数据导致的问题

//多个线程同时操作临界数据,导致临界数据脏的问题
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int n = 0;
void* func1(void* arg){
	for(int i = 0; i < 25000000; i++)
		n++;
}

void* func2(void* arg){
	for(int i = 0; i < 25000000; i++)
		n++;
}

int main(){
	pthread_t p1,p2,p3,p4;

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);
	pthread_create(&p3,NULL,func1,NULL);
	pthread_create(&p4,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束
	pthread_join(p3,NULL); 	//等待线程结束
	pthread_join(p4,NULL); 	//等待线程结束

	printf("n: %d\n",n);	// <= 100000000

	return 0;
}

4.3 使用线程同步解决临界数据脏的方式

内核态用户态
原子锁(atomic)读写锁(rwlock)
自旋锁(spin)互斥锁(mutex)
信号量(sem)临界变量(cond)

4.3.1 原子锁(atomic)

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

#define atomic_inc(x)  __sync_fetch_and_add(x,1)

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++)
		atomic_inc((int*)arg);
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++)
		atomic_inc((int*)arg);
}

int main(){
	pthread_t p1,p2;
	int n = 0;
	
	pthread_create(&p1,NULL,func1,&n);
	pthread_create(&p2,NULL,func2,&n);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	return 0;
}

4.3.2 自旋锁(spin)

自旋锁的建立消耗的资源少, 但是当线程阻塞时, 它会一直循环不断地检查锁是否可用, 因此当线程处于阻塞状态下, 消耗的资源相对较多

//自旋锁(spin)的使用
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int n = 0;
//0. 定义自旋锁
pthread_spinlock_t spin;

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_spin_lock(&spin);
		//操作
		n++;
		//3. 解锁
		pthread_spin_unlock(&spin);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_spin_lock(&spin);
		//操作
		n++;
		//3. 解锁
		pthread_spin_unlock(&spin);
	}
}

int main(){
	pthread_t p1,p2;

	//1. 初始化自旋锁
	pthread_spin_init(&spin,PTHREAD_PROCESS_PRIVATE);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	//4. 销毁自旋锁
	pthread_spin_destroy(&spin);

	return 0;
}

4.3.3 信号量(sem)

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

int n = 0;
//0. 定义信号量
sem_t sem;

void* func1(void* arg){
	//信号量值减一
	if(0 == sem_wait(&sem)){
		for(int i = 0; i < 50000000; i++){
			n++;
			//信号量值加一
			sem_post(&sem);
		}
	}	
}

int main(){
	pthread_t p1,p2;

	//1. 初始化信号量
	//参数二 0 当前进程使用   1  多个进程间共享
	sem_init(&sem, 0, 2);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func1,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);

	//销毁信号量
	sem_destroy(&sem);	

	return 0;
}

4.3.4 读写锁(rwlock)

//读写锁(rwlock)的使用	读读相容
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

int n = 0;
//0. 定义读写锁变量
pthread_rwlock_t rwlock;

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(读锁)
		pthread_rwlock_rdlock(&rwlock);
		//pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(读锁)
		pthread_rwlock_rdlock(&rwlock);
		//pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

int main(){
	pthread_t p1,p2;

	//1. 初始化读写锁
	pthread_rwlock_init(&rwlock,NULL);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	//4. 销毁读写锁
	pthread_rwlock_destroy(&rwlock); 

	return 0;
}

 读读相容 

 

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(读锁)
		pthread_rwlock_rdlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(写锁)
		pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

 读写相斥

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(写锁)
		pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(写锁)
		pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

写写相斥        

4.3.5 互斥锁(mutex)

//互斥锁(mutex)的使用
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int n = 0;
//0. 定义互斥变量
pthread_mutex_t mutex;

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_mutex_lock(&mutex);
		//操作
		n++;
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_mutex_lock(&mutex);
		//操作
		n++;
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

int main(){
	pthread_t p1,p2;

	//1. 初始化互斥量
	pthread_mutex_init(&mutex,NULL);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);
	//4. 销毁互斥锁
	pthread_mutex_destroy(&mutex);

	return 0;
}

 

4.3.6 临界变量(cond)

//临界(条件)变量(cond)的使用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

int n = 0;
//0. 定义互斥变量
pthread_mutex_t mutex;
//0. 定义临界变量
pthread_cond_t cond;

void* func1(void* arg){
	for(int i = 0; i <= 5; i++){
		//2. 上锁,临界变量等待
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		//操作
		printf("线程1 ------ %d\n",n++);
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

void* func2(void* arg){
	for(int i = 0; i <= 5; i++){
		//2. 上锁,临界变量等待
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		//操作
		printf("线程2 ====== %d\n",n++);
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

int main(){
	pthread_t p1,p2,p3,p4;

	//1. 初始化互斥锁和临界变量
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	//4. 发信号给临界变量
	for(int i = 0; i < 13; i++){
		//解除全部线程的阻塞状态(一次)
		//pthread_cond_broadcast(&cond);
		//解除至少一个线程的阻塞状态(一次)
		pthread_cond_signal(&cond);
		//pthread_cond_signal(&cond);
		sleep(1);
	}
	
	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	//5. 销毁互斥锁和临界变量
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);

	return 0;
}

举报

相关推荐

0 条评论