Linux线程

一.线程介绍/概念

1.进程与线程

概念:一个进程至少一个线程,可以多个线程同时处理独立的任务.

目的:程序运行时(进程)同一时刻做不同的功能,一个线程处理一个任务.

进程是资源管理最小单位(CPU占用时间及内存空间大小)

线程是程序执行的最小单位

进程创建/运行需要消耗硬件资源及其时间,线程是基于进程已分配的资源运行的.进程是线程的容器

线程运行开销小,进程运行开销大 程序是静态的,运行的程序称之为进程,实际处理任务的是线程

多进程比多线程健壮,单一的进程结束不会影响其他进程,而线程反之只要任意一个线程(同一进程下)结束,整个进程就结束

缺点:多进程占用资源多,效率低

进程有独立的地址空间,同一进程内线程共享进程的地址空间

2.使用线程的理由

线程是节俭的多任务操作方式,同一进程内线程共享进程的地址空间;而进程则是去开辟独立的空间所有进程相对于线程则是昂贵的

线程间通信共享地址内的数据

多线程是一种多任务多并发的工作方式

​ 提高应用的响应

​ 使多CPU系统更加有效(利用率)

​ 改善程序结构(模块化运行,就像函数一样,便于理解和修改代码)

二.线程的API及其应用

多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。

其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。

线程操作又分线程的创建,退出,等待 3 种。

互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。

条件操作有 5 种操作:创建,销毁,触发,广播和等待。

在这里插入图片描述

线程操作

pthread_create函数原型

#include <pthread.h>

// 线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
thread		指针实际功能的容器,线程指针就是去绑定实际运行功能的	pthread_t类型的指针,指向实际运行函数(功能)
attr		线程属性	一般NULL默认属性
*start_routine		执行功能函数的指针(实际功能)
*arg		传递参数(无类型指针)多个就用结构体\数组
/*
pthread_t *thread:
这是一个指向 pthread_t 类型变量的指针,用于存储新创建的线程的标识符(ID)。当 pthread_create 成功创建一个新线程后,这个变量的值会被设置为新线程的ID。这个线程ID在后续的操作中可以用来引用这个线程,比如通过 pthread_join 函数等待线程结束。
const pthread_attr_t *attr:
这是一个指向 pthread_attr_t 类型变量的指针,用于设置新线程的属性。线程属性包括线程的堆栈大小、调度策略、分离状态等。如果不需要特别设置线程属性,可以将此参数设置为 NULL,此时线程将使用默认的属性。如果需要设置线程属性,可以使用 pthread_attr_init 和 pthread_attr_setxxx 等函数进行初始化和设置。
void *(*start_routine) (void *):
这是一个函数指针,指向线程执行体的起始地址,也就是线程开始执行后要运行的代码块。这个函数在线程启动时由系统自动调用,并且在线程退出前都会一直运行。该函数的返回类型应为 void *,表示它可以返回任何类型的指针。函数的参数类型也为 void *,意味着可以传递任何类型的指针给线程函数。
void *arg:
这是一个指向任何类型的指针,用于传递给线程执行体(即 start_routine 所指向的函数)的参数。这个参数可以是任何类型的指针,包括结构体、整数、字符数组等。如果不需要传递参数给线程函数,可以将此参数设置为 NULL。如果需要传递多个参数,可以使用结构体等数据类型进行封装。
*/

/*
当pthread_create成功返回时,由thread指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。

  新创建的线程从start_routine函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_routine函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
*/

/*
线程标识符(thread)必须是一个有效的指针,并且在线程结束之前不应该被释放。
如果没有特殊要求,通常可以将线程属性(attr)设置为 NULL,以使用默认的线程属性。
传递给线程函数的参数(arg)必须被声明为 void * 类型。
pthread_create 函数调用成功后,新线程并不一定会立即开始执行。它通常会被放入系统调度器中等待CPU资源,并在调度到时才真正开始执行。
*/

// 返回值:若成功返回0,否则返回错误编号

pthread_exit函数原型

#include <pthread.h>

// 线程退出
void pthread_exit(void *retval);

retval:这是一个指向某种类型的指针,通常用于将线程的退出状态或结果传递回其他线程或线程的主调函数。这个返回值可以通过 pthread_join 函数来获取。retval 可以是任何类型的数据的指针,这取决于你希望从线程中返回什么信息。如果不需要返回任何信息,可以将 retval 设置为 NULL.

/*
线程终止:调用 pthread_exit 会立即停止当前线程的执行,但不会释放线程所占用的所有资源。资源的释放通常会在其他线程调用 pthread_join 时发生,或者当程序结束时由操作系统自动处理。
清理函数:与 atexit 类似,POSIX 线程库也提供了 pthread_cleanup_push 和 pthread_cleanup_pop 函数,用于在线程退出时执行清理操作。这些清理操作可以包括释放动态分配的内存、关闭打开的文件等。
与 return 的区别:在线程函数中,直接调用 return 也会结束线程的执行,但其行为和 pthread_exit 略有不同。使用 return 会将返回值作为线程的退出状态,但这个状态通常不容易被其他线程获取。而 pthread_exit 允许你明确地指定一个返回值,并通过 pthread_join 来获取这个值。
与进程退出:pthread_exit 只结束一个线程的执行,而不是整个进程。整个进程会在所有线程都结束执行,并且主线程(通常是调用 main 函数的线程)也结束时才终止。
*/

pthread_join函数原型

#include <pthread.h>

// 线程等待
int pthread_join(pthread_t thread, void **retval);

thread:这是你想要等待的线程的标识符。这个标识符通常是通过 pthread_create 函数创建线程时返回的。
retval:这是一个指向指针的指针,用于接收线程的返回值。这个返回值是线程调用 pthread_exit 时传递的参数。如果你对线程的返回值不感兴趣,可以将 retval 设置为 NULL/*
线程同步:pthread_join 是一种线程同步机制,它使调用它的线程阻塞,直到指定的线程 thread 调用 pthread_exit 或因为其他原因结束。如果 thread 尚未结束,调用 pthread_join 的线程会阻塞,直到 thread 结束。

可重用性:当一个线程被其他线程通过 pthread_join 成功连接后,它的线程 ID 和相关资源可以被系统回收再利用。

多次调用:不应该对同一个线程多次调用 pthread_join。一旦一个线程已经被成功连接,再次调用 pthread_join 会导致未定义的行为。

分离线程:如果一个线程被设置为分离状态(通过 pthread_detach),那么它结束时会自动释放其资源,并且不能被其他线程 pthread_join。尝试连接一个已分离的线程将导致错误。

死锁:如果两个线程互相等待对方结束(即线程 A 调用 pthread_join 等待线程 B,而线程 B 调用 pthread_join 等待线程 A),则会发生死锁。在设计多线程程序时,必须小心避免这种情况。
*/

// 返回:若成功返回0,否则返回错误编号
#include <stdio.h>
#include <pthread.h>


void *func1(void *argc)
{
	static char *str = "thread succrssful.";
	printf("param:%d\n",*((int*)argc));
	
	pthread_exit((void*)str);	
}

int main()
{
	int ret;
	int param = 100;
	pthread_t t1;
	
	char *str = NULL;
	
	ret = pthread_create(&t1,NULL,func1,(void*)&param);
	if(ret == 0){
		printf("thread the successful.\n");
	}
	
	pthread_join(t1,(void**)&str);
	
	printf("%s\n",str);

	return 0;
}

pthread_detach函数原型(不常用)

#include <pthread.h>

// 线程脱离
int pthread_detach(pthread_t thread);

pthread_t thread		这是需要被设置为可分离状态的线程的标识符。线程标识符是在创建线程时由pthread_create函数返回的。通过提供这个线程ID,pthread_detach函数能够确定哪个线程应该被标记为可分离的。

// 返回值	如果函数成功执行,返回0。	如果函数执行失败,返回非零错误码。

pthread_self函数原型(常用)

#include <pthread.h>

// 线程ID获取
pthread_t pthread_self(void);	// pthread_self函数不接受任何参数,它是一个无参数函数。

// 返回值	pthread_self函数返回一个类型为pthread_t的值,这个值代表调用线程的线程ID。pthread_t是POSIX定义的一个类型,用于唯一标识每个创建的线程。

intptr_t 拓展

intptr_t 是一个有符号整数类型。
它的大小足以存储任何对象指针的值。这意味着在32位系统上,intptr_t 通常是32位的;在64位系统上,它是64位的。
当你需要将指针转换为整数或将整数转换为指针时,应使用 intptr_t 而不是 int 或其他整数类型,因为 int 的大小在不同的系统和编译器上可能不同,这可能导致兼容性问题。
参数
intptr_t 本身不是一个函数,所以没有参数。但是,当使用 intptr_t 进行类型转换时,你实际上是在将一个值(通常是指针或整数)转换为 intptr_t 类型,或者将 intptr_t 类型的值转换回原始类型。

例如,(intptr_t)pointer 将一个指针 pointer 转换为 intptr_t 类型的值,而 (void*)(intptr_t)num 将一个 intptr_t 类型的值 num 转换回 void* 类型的指针。在进行这样的转换时,必须确保转换是安全的,即转换后的值不会丢失信息或引起未定义的行为。


size_tsize_t是一个无符号整数类型,通常用于表示对象的大小,或用于数组的索引。它是sizeof操作符返回的类型。如果设备的地址空间是32位,size_t通常是一个32位无符号整数;如果地址空间是64位,则是一个64位无符号整数。
ptrdiff_tptrdiff_t是一个有符号整数类型,用于表示两个指针之间的差异。当你需要对两个指针进行相减操作时,得到的结果通常是ptrdiff_t类型。与size_t类似,其大小也取决于设备的地址空间。
uintptr_tuintptr_t是一个无符号整数类型,与intptr_t类似,但其值是无符号的。它的大小也足以存储任何数据指针的值。在某些情况下,当你需要确保指针转换为一个无符号整数时,可以使用uintptr_t

实例

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

struct text
{
	char name[36];
	int age;
	char sex;
};

void *function(void *argc)
{
	struct text *data;
	data = (struct text *)argc;

	printf("name:%s\n",data->name);
	printf("age:%d\n",data->age);
	printf("sex:%c\n",data->sex);
	printf("func1:%ld\n",pthread_self());
	
	
	char *str = malloc(strlen("nianxing_su")+1);
	
	strcpy(str,"nianxing_su");
	
	pthread_exit((void*)str);
	
	printf("*************************************\n");
}

int main(int argc,char* argv[])
{
	struct text data;
	strcpy(data.name,"nianxing_su");
	data.age = 27;
	data.sex = 'G';
	pthread_t t1;
	pthread_create(&t1,NULL,&function,(void *)&data);
	
	printf("main:%ld\n",pthread_self());
	void * x;
	
	pthread_join(t1,&x);

	printf("main:%ld\n",pthread_self());
	char * y = (char*)x;
	printf("x:%s\n",y);
	
	free(y);
	return 0;
}

实例

#include <stdio.h>
#include <pthread.h>	// Thread header file
#include <stdint.h>	// intptr_t header file


void *func1(void *argc)
{
	int *z = (int*)(argc);	// DATA forcing
	printf("%d\n",*z);
	intptr_t pthid = pthread_self();	// get thread ID
	printf("pthid:%ld.\n",pthid);		// print thread ID
	pthread_exit((void*)pthid);		// the thread exits and returns to the thread ID 
}


int main()
{
	int ret;	// bound thread return value
	int param = 100;	// incoming data
	pthread_t t1;		// create thread
	
	ret = pthread_create(&t1,NULL,&func1,(void*)&param);	// thread binding task/assignment task
	if(ret != 0){	// thread binding error
		perror("error");	// print error message
	}else{	// thread binding successful
		void *data;	// define thread return variables
		pthread_join(t1,&data);		// the thread waits and gets the renurn value
		
//		long int num = (intptr_t)data;
		intptr_t num = (intptr_t)data;		// data forcing
		
		printf("pthid:%ld,binding successful.\n",num);	// thread binding successful
		printf("mainpthid:%ld.\n",pthread_self());	// get the main thread ID and print
		
	}
	return 0;
}

互斥锁(主线程也就是main线程不参与加锁解锁)

pthread_mutex_init()函数原型

#include <pthread.h>

// 互斥锁的初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
mutex	指向互斥锁的指针
attr	属性	一般为NULL

返回值		0成功,0值是失败,返回错误码

用于初始化互斥锁。在多线程环境中,通过初始化互斥锁,可以实现对共享资源的互斥访问,防止多个线程同时访问和修改同一资源,从而避免数据竞争和不一致的问题。		**多线程运行时,就是一个线程结束了,再执行下一个线程

pthread_mutex_destroy()函数原型

#include <pthread.h>

// 销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex	指向互斥锁的指针

返回值		0成功,0值是失败,返回错误码

用于销毁互斥锁。当不再需要互斥锁时,应调用此函数销毁它,并释放相关资源。

pthread_mutex_lock()函数原型

#include <pthread.h>

// 加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex	指向互斥锁的指针

返回值		0成功,0值是失败,返回错误码

用于获取互斥锁。当线程调用此函数时,如果互斥锁当前未被其他线程持有,则线程将获得锁并继续执行;如果锁已被其他线程持有,则线程将被阻塞,直到锁被释放。

pthread_mutex_unlock函数原型

#include <pthread.h>

// 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex	指向互斥锁的指针

返回值		0成功,0值是失败,返回错误码

用于释放互斥锁。当线程完成对共享资源的访问后,应调用此函数释放锁,以便其他线程可以获取锁并访问资源。

pthread_mutex_trylock函数原型(不常用)

#include <pthread.h>

// 加锁	锁是空闲时使用锁,并且非阻塞方式运行
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex	指向互斥锁的指针

返回值		0成功,0值是失败,返回错误码

特点是非阻塞性,即如果互斥锁已经被其他线程锁定,pthread_mutex_trylock不会使当前线程阻塞等待锁释放,而是立即返回一个值来描述互斥锁的状况。

尝试获取锁:当调用pthread_mutex_trylock时,它会检查指定的互斥锁是否已经被其他线程锁定。如果锁是空闲的(即未被任何线程持有),则当前线程会立即获取该锁,并继续执行后续的代码。
非阻塞特性:与pthread_mutex_lock不同,pthread_mutex_trylock不会使线程在锁被其他线程持有时进入阻塞状态。这意味着调用pthread_mutex_trylock的线程不会因为等待锁释放而被挂起,从而能够继续执行其他任务或进行其他操作。
返回值:pthread_mutex_trylock的返回值用于指示尝试获取锁的结果。如果成功获取锁,则返回0;如果锁已经被其他线程持有,则返回一个错误码(通常是EBUSY),表示当前线程未能获取锁。

实例

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


pthread_mutex_t mutex;	// define a mutex lock

void *func1(void *arg)
{
	pthread_mutex_lock(&mutex);	// add a mutex lock
	
	printf("t1:%ld thread is create.\n",(unsigned long)pthread_self());	// t1 the thread id
	printf("t1 param is %d\n",*((int*)arg));	// parameter
	
	pthread_mutex_unlock(&mutex);	// unlock the mutex
	return NULL;	// the return value is necessary
}

void *func2(void *arg)
{
	pthread_mutex_lock(&mutex);	// add a mutex lock

	printf("t2:%ld thread is create.\n",(unsigned long)pthread_self());	// t2 the thread id
	printf("t2 param is %d\n",*((int*)arg));	// parameter
	
	pthread_mutex_unlock(&mutex);	// unlock the mutex
	return NULL;	// the return value is necessary
}

/*
Thread exclusive operation.
*/
int main()
{
	int ret;
	int param = 99;
	
	pthread_t t1;		// define thread space.
	pthread_t t2;
	
	pthread_mutex_init(&mutex,NULL);	// mutex initialization
	
	ret = pthread_create(&t1,NULL,func1,(void*)&param);	// thread-space binding to a specific function or function.
	if(ret == 0){	// if ret is 0,the binding succeeds.othrwise,the binding fails.
		printf("t1 binding succeeds.\n");
	}
	
	ret = pthread_create(&t2,NULL,func2,(void*)&param);
	if(ret == 0){
		printf("t2 binding succeeds.\n");
	}
	
	printf("main:%ld\n",(unsigned long)pthread_self());	// main the thread id
	
	
	pthread_join(t1,NULL);	// the thread is blocked,and the program executes down after the thread completes
	pthread_join(t2,NULL);
	
	pthread_mutex_destroy(&mutex);	// destroy the mutex
	return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int num = 0;

pthread_mutex_t mutex;	// define a mutex lock

void *func1(void *arg)
{
	pthread_mutex_lock(&mutex);	// add a mutex lock
	
	for(int i = 0; i < 5;i++){
		printf("t1:%ld thread is create.\n",(unsigned long)pthread_self());	// t1 the thread id
		printf("t1 param is %d\n",*((int*)arg));	// parameter
		sleep(1);
	}
	
	pthread_mutex_unlock(&mutex);	// unlock the mutex
	return NULL;	// the return value is necessary
}

void *func2(void *arg)
{
	pthread_mutex_lock(&mutex);	// add a mutex lock

	printf("t2:%ld thread is create.\n",(unsigned long)pthread_self());	// t2 the thread id
	printf("t2 param is %d\n",*((int*)arg));	// parameter
	
	pthread_mutex_unlock(&mutex);	// unlock the mutex
	return NULL;	// the return value is necessary
}

/*
Thread exclusive operation.
*/
int main()
{
	int ret;
	int param = 99;
	
	pthread_t t1;		// define thread space.
	pthread_t t2;
	
	pthread_mutex_init(&mutex,NULL);	// mutex initialization
	
	ret = pthread_create(&t1,NULL,func1,(void*)&param);	// thread-space binding to a specific function or function.
	if(ret == 0){	// if ret is 0,the binding succeeds.othrwise,the binding fails.
		printf("t1 binding succeeds.\n");
	}
	
	ret = pthread_create(&t2,NULL,func2,(void*)&param);
	if(ret == 0){
		printf("t2 binding succeeds.\n");
	}
	
	printf("main:%ld\n",(unsigned long)pthread_self());	// main the thread id
	
	
	pthread_join(t1,NULL);	// the thread is blocked,and the program executes down after the thread completes
	pthread_join(t2,NULL);
	
	pthread_mutex_destroy(&mutex);	// destroy the mutex
	return 0;
}

满足条件,线程退出

必须再线程退出之前释放锁/解锁要不然就会造成死锁
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int num = 0;

pthread_mutex_t mutex;	// define a mutex lock

void *func1(void *arg)
{
	pthread_mutex_lock(&mutex);	// add a mutex lock
	
	while(1){
		printf("t1:%ld thread is create.\n",(unsigned long)pthread_self());	// t1 the thread id
		printf("t1 param is %d\n",*((int*)arg));	// parameter
		if(num == 5){	
			pthread_mutex_unlock(&mutex);	// unlock the mutex	// the lock must be released before the thread exits(unlock)
            
			pthread_exit(NULL);
		}
		num++;
		sleep(1);
	}
	
	// the program exits first and does not lock,which will cause the program to be unable to enter other threads
//	pthread_mutex_unlock(&mutex);	// can not unlock it here
	return NULL;	// the return value is necessary
}

void *func2(void *arg)
{
	pthread_mutex_lock(&mutex);	// add a mutex lock
	while(1){
		printf("t2:%ld thread is create.\n",(unsigned long)pthread_self());	// t2 the thread id
		printf("t2 param is %d\n",*((int*)arg));	// parameter
		sleep(1);
	}
	pthread_mutex_unlock(&mutex);	// unlock the mutex
	return NULL;	// the return value is necessary
}

/*
Thread exclusive operation.
*/
int main()
{
	int ret;
	int param = 99;
	
	pthread_t t1;		// define thread space.
	pthread_t t2;
	
	pthread_mutex_init(&mutex,NULL);	// mutex initialization
	
	ret = pthread_create(&t1,NULL,func1,(void*)&param);	// thread-space binding to a specific function or function.
	if(ret == 0){	// if ret is 0,the binding succeeds.othrwise,the binding fails.
		printf("t1 binding succeeds.\n");
	}
	
	ret = pthread_create(&t2,NULL,func2,(void*)&param);
	if(ret == 0){
		printf("t2 binding succeeds.\n");
	}
	
	printf("main:%ld\n",(unsigned long)pthread_self());	// main the thread id
	
	
	pthread_join(t1,NULL);	// the thread is blocked,and the program executes down after the thread completes
	pthread_join(t2,NULL);
	
	pthread_mutex_destroy(&mutex);	// destroy the mutex
	return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

int num = 0;

pthread_mutex_t mutex;	// define a mutex lock

void *func1(void *arg)
{
	printf("t1:%ld thread is create.\n",(unsigned long)pthread_self());	// t1 the thread id
	printf("t1 param is %d\n",*((int*)arg));	// parameter
		
	pthread_mutex_lock(&mutex);	// add a mutex lock	
	while(1){	
		if(num == 3){	
			pthread_mutex_unlock(&mutex);	// unlock the mutex	// the lock must be released before the thread exits(unlock)
			printf("exit***************\n");
		//	pthread_exit(NULL);	// exit thread
			exit(0);	// exit process
		}
		printf("t1:%d\n",num++);
		
		sleep(1);
	}
	
	// the program exits first and does not lock,which will cause the program to be unable to enter other threads
//	pthread_mutex_unlock(&mutex);	// can not unlock it here
	return NULL;	// the return value is necessary
}

void *func2(void *arg)
{
		

	
	printf("t2:%ld thread is create.\n",(unsigned long)pthread_self());	// t2 the thread id
	printf("t2 param is %d\n",*((int*)arg));	// parameter
		
	while(1){	
		printf("t2:%d\n",num);
		pthread_mutex_lock(&mutex);	// add a mutex lock
		num++;
		pthread_mutex_unlock(&mutex);	// unlock the mutex
		sleep(1);
	}
	
		
	return NULL;	// the return value is necessary
}

/*
Thread exclusive operation.
*/
int main()
{
	int ret;
	int param = 99;
	
	pthread_t t1;		// define thread space.
	pthread_t t2;
	
	pthread_mutex_init(&mutex,NULL);	// mutex initialization
	
	ret = pthread_create(&t1,NULL,func1,(void*)&param);	// thread-space binding to a specific function or function.
	if(ret == 0){	// if ret is 0,the binding succeeds.othrwise,the binding fails.
		printf("t1 binding succeeds.\n");
	}
	
	ret = pthread_create(&t2,NULL,func2,(void*)&param);
	if(ret == 0){
		printf("t2 binding succeeds.\n");
	}
	
	printf("main:%ld\n",(unsigned long)pthread_self());	// main the thread id
	
	
	pthread_join(t1,NULL);	// the thread is blocked,and the program executes down after the thread completes
	pthread_join(t2,NULL);
	
	pthread_mutex_destroy(&mutex);	// destroy the mutex
	return 0;
}

脚本1

在这里插入图片描述

./pth
./pth
./pth
./pth
./pth
./pth
./pth	// 执行五次pth进程	

/*
	chmod +x syh.sh		// 脚本文件加执行权限
	./syh.sh	//执行脚本文件
*/

脚本2(c程序)

int main()
{
	int i = 0;
	for(i;i<100;i++){
		system("./pth");	// script file execution program
	}
	return 0;
}

/*
system("./pth");
参数就一个文件的路径和执行文件		执行文件的路径

要关闭这个进程无法使用ctrl+c
必须杀进程
*/

在这里插入图片描述

什么是死锁,死锁是如何造成的,怎么去解决这个问题

死锁:互斥锁的使用不当,导致多个线程无法进行下一步的执行
如何造成的:多个锁,其中某一个锁被其他线程拿了未释放,阻塞再那个位置
如何避免:再退出线程时,先释放掉锁(解锁)
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

int num = 0;

pthread_mutex_t mutex1;	// define a mutex lock
pthread_mutex_t mutex2;

void *func1(void *arg)
{
	pthread_mutex_lock(&mutex1);	// add a mutex lock	
	sleep(1);	// wait for another to take the lock
	pthread_mutex_lock(&mutex2);	// lock 2 is used by another thread
	
	printf("t1:%ld thread is create.\n",(unsigned long)pthread_self());	// t1 the thread id
	printf("t1 param is %d\n",*((int*)arg));	// parameter
		
	
	while(1){	
		if(num == 3){	
			pthread_mutex_unlock(&mutex1);	// unlock the mutex	// the lock must be released before the thread exits(unlock)
			printf("exit***************\n");
		//	pthread_exit(NULL);	// exit thread
			exit(0);	// exit process
		}
		printf("t1:%d\n",num++);
		
		sleep(1);
	}
	pthread_mutex_unlock(&mutex2);	// unlock the mutex
}

void *func2(void *arg)
{
		
	pthread_mutex_lock(&mutex2);
	sleep(1);
	pthread_mutex_lock(&mutex1);	// add a mutex lock
	printf("t2:%ld thread is create.\n",(unsigned long)pthread_self());	// t2 the thread id
	printf("t2 param is %d\n",*((int*)arg));	// parameter
		
	while(1){	
		printf("t2:%d\n",num);
		
		num++;
		
		sleep(1);
	}
	pthread_mutex_unlock(&mutex1);	// unlock the mutex
	pthread_mutex_unlock(&mutex2);	// unlock the mutex
}

/*
Thread mutex operation
*/
int main()
{
	int ret;
	int param = 99;
	
	pthread_t t1;		// define thread space.
	pthread_t t2;
	
	pthread_mutex_init(&mutex1,NULL);	// mutex initialization
	pthread_mutex_init(&mutex2,NULL);
	
	ret = pthread_create(&t1,NULL,func1,(void*)&param);	// thread-space binding to a specific function or function.
	if(ret == 0){	// if ret is 0,the binding succeeds.othrwise,the binding fails.
		printf("t1 binding succeeds.\n");
	}
	
	ret = pthread_create(&t2,NULL,func2,(void*)&param);
	if(ret == 0){
		printf("t2 binding succeeds.\n");
	}
	
	printf("main:%ld\n",(unsigned long)pthread_self());	// main the thread id
	
	
	pthread_join(t1,NULL);	// the thread is blocked,and the program executes down after the thread completes
	pthread_join(t2,NULL);
	
	pthread_mutex_destroy(&mutex1);	// destroy the mutex
	pthread_mutex_destroy(&mutex2);	
	return 0;
}

选择条件

pthread_cond_init函数原型

#include <pthread.h>

// 条件变量初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
cond	一个指针指向条件变量
attr	属性还是互斥锁指针?/一个指针指向互斥锁变量??

返回值:
如果函数成功执行,则返回 0。
如果出现错误,则返回错误码。常见的错误码包括 EINVAL(如果 attr 不是一个有效的属性对象且不为 NULL)和 ENOMEM(如果内存不足,无法初始化条件变量)。

初始化条件变量。条件变量通常与互斥锁一起使用,以实现线程间的同步。

pthread_cond_destroy函数原型

#include <pthread.h>

// 条件变量销毁
int pthread_cond_destroy(pthread_cond_t *cond);
cond	一个指针指向条件变量要销毁的条件

返回值:
如果函数成功执行,则返回 0。
如果出现错误,则返回错误码。常见的错误码包括 EBUSY(如果条件变量仍然被等待或信号发送),这表示在销毁条件变量之前,必须确保没有任何线程正在等待它,也没有线程准备发送信号到它。

销毁一个条件变量会释放与其关联的所有资源。

pthread_cond_signal函数原型

#include <pthread.h>

// 条件触发(只唤醒一个)		一对一(与pthread_cond_wait/timedwait函数配合使用)
int pthread_cond_signal(pthread_cond_t *cond);
cond	一个指针指向条件变量唤醒一个等待的条件(该条件变量至少应该有一个线程正在等待(通过 pthread_cond_wait 或 pthread_cond_timedwait)。)

返回值:
如果函数成功执行,则返回 0。
如果出现错误,则返回错误码。常见的错误码包括 EINVAL(如果 cond 不是一个有效的条件变量)。

用于唤醒一个在条件变量上等待的线程。当某个条件成立时,通常调用此函数来通知一个或多个等待的线程继续执行。


/*
当条件成立时,另一个线程通过 pthread_cond_signal 唤醒等待的线程。注意,在调用 pthread_cond_signal 之前和之后,都需要对互斥锁 mutex 进行加锁和解锁操作,以确保条件变量的正确同步。

pthread_cond_signal 只会唤醒一个等待的线程,如果有多个线程在等待,则选择哪个线程被唤醒是未定义的。如果想要唤醒所有等待的线程,可以使用 pthread_cond_broadcast 函数。
*/

pthread_cond_broadcast函数原型

#include <pthread.h>

// 条件广播(唤醒多个)	一对多(与pthread_cond_wait/timedwait函数配合使用)
int pthread_cond_broadcast(pthread_cond_t *cond);
cond	一个指针指向条件变量唤醒多个等待的条件

返回值:
如果函数成功执行,则返回 0。
如果出现错误,则返回错误码。常见的错误码包括 EINVAL(如果 cond 不是一个有效的条件变量)。

唤醒所有在特定条件变量上等待的线程。这通常用于通知所有等待的线程某个条件已经满足,它们可以继续执行。

pthread_cond_wait/pthread_cond_timedwait函数原型

#include <pthread.h>

// 线程等待
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
/*
使线程等待某个条件成立。当线程调用这个函数时,它会解锁互斥锁,并进入等待状态,直到另一个线程通过 pthread_cond_signal 或 pthread_cond_broadcast 唤醒它。
*/

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
cond	指向要等待的条件变量的指针。
mutex	指向互斥锁的指针
abstime		一个指向 timespec 结构的指针,该结构指定了线程等待条件变量的绝对			时间。如果在这个时间之前条件没有被满足,线程将停止等待并返回错误 			ETIMEDOUT。timespec 结构包含秒(tv_sec)和纳秒(tv_nsec)字段。

返回值:
如果函数成功执行,则返回 0。
如果出现错误,如超时或条件变量无效,则返回错误码。


条件变量 cond 上等待,并指定了一个超时时间。如果条件在超时时间内没有被满足,pthread_cond_timedwait 将返回错误码 ETIMEDOUT,线程可以据此处理超时情况。注意,与 pthread_cond_wait 一样,在调用 pthread_cond_timedwait 之前和之后,都需要对互斥锁 mutex 进行加锁和解锁操作。

实例

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

int num = 0;

pthread_mutex_t mutex;	// define a mutex lock(create a new mutex variable)/定义(新建)互斥锁
pthread_cond_t cond;	// definite conditions(create a new condition variable)/定义(新建)条件

void *func1(void *arg)
{
	printf("t1:%ld thread is create.\n",(unsigned long)pthread_self());	// t1 the thread id
	printf("t1 param is %d\n",*((int*)arg));	// parameter
	
	while(1){	
		pthread_cond_wait(&cond,&mutex);		// the thread waits,teiggering it to execute further when other threads meet certain conditions(线程等待,在其他线程满足一定条件时触发该线程往下执行)
		printf("t1:%d\n",num);
		printf("exit***************\n");
		num = 0;
		sleep(1);
	}
	return NULL;	// the return value is necessary
}

void *func2(void *arg)
{
		

	
	printf("t2:%ld thread is create.\n",(unsigned long)pthread_self());	// t2 the thread id
	printf("t2 param is %d\n",*((int*)arg));	// parameter
		
	while(1){	
		printf("t2:%d\n",num);
		pthread_mutex_lock(&mutex);	// add a mutex lock
		num++;
		pthread_mutex_unlock(&mutex);	// unlock the mutex
		if(num == 3){	// when the condition is met,other waiting threads are started(满足条件时,启动其他等待线程)
			pthread_cond_signal(&cond);	// trgger the waiting thread one on one(一对一触发等待线程)
			
		}
		
		sleep(1);
	}
	
		
	return NULL;	// the return value is necessary
}

/*
Thread exclusive operation.
*/
int main()
{
	int ret;
	int param = 99;
	
	pthread_t t1;		// define thread space.
	pthread_t t2;
	
	pthread_mutex_init(&mutex,NULL);	// mutex initialization
	pthread_cond_init(&cond,NULL);	// conditional initialization
	
	ret = pthread_create(&t1,NULL,func1,(void*)&param);	// thread-space binding to a specific function or function.
	if(ret == 0){	// if ret is 0,the binding succeeds.othrwise,the binding fails.
		printf("t1 binding succeeds.\n");
	}
	
	ret = pthread_create(&t2,NULL,func2,(void*)&param);
	if(ret == 0){
		printf("t2 binding succeeds.\n");
	}
	
	pthread_join(t1,NULL);	// the thread is blocked,and the program executes down after the thread completes
	pthread_join(t2,NULL);
	
	pthread_mutex_destroy(&mutex);	// destroy the mutex
	pthread_cond_destroy(&cond);	// conditional destruction
	return 0;
}

宏初始化互斥锁及其条件

使用宏进行初始化
        在使用互斥变量、条件变量前都必须进行初始化,可以分别置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量)、PTHREAD_COND_INITIALIZER(只对静态分配的条件变量),也可以通过调用pthread_mutex_init、pthread_cond_init函数进行初始化。如果动态地分配互斥量、条件变量(例如通过调用malloc函数),那么在释放内存前需要调用pthread_mutex_destroy,条件变量可以使用pthread_cond_destroy函数对条件变量进行去除初始化(deinitialize)。

动态初始化:

pthread_mutex_t mutex;  //dynamic init
pthread_cond_t cond;      //dynamic init

主函数中必须:

pthread_mutex_init(&mutex, NULL);   //dynamic init
pthread_cond_init(&cond, NULL);       //dynamic init

使用宏进行初始化(静态初始化):

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // static init
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // static init
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

int num = 0;

// Macro definition initializes the mutex lock and its conditions(宏定义初始化互斥锁及其条件)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;	// define a mutex lock(create a new mutex variable)/定义(新建)互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;	// definite conditions(create a new condition variable)/定义(新建)条件

void *func1(void *arg)
{
	printf("t1:%ld thread is create.\n",(unsigned long)pthread_self());	// t1 the thread id
	printf("t1 param is %d\n",*((int*)arg));	// parameter
	
	static int cnt = 0;
	while(1){	
		pthread_cond_wait(&cond,&mutex);		// the thread waits,teiggering it to execute further when other threads meet certain conditions(线程等待,在其他线程满足一定条件时触发该线程往下执行)
		printf("t1:%d\n",num);
		printf("exit***************\n");
		num = 0;
		sleep(1);
		if(cnt++ == 9){
			exit(1);
		}
	}
	return NULL;	// the return value is necessary
}

void *func2(void *arg)
{
		

	
	printf("t2:%ld thread is create.\n",(unsigned long)pthread_self());	// t2 the thread id
	printf("t2 param is %d\n",*((int*)arg));	// parameter
		
	while(1){	
		printf("t2:%d\n",num);
		pthread_mutex_lock(&mutex);	// add a mutex lock
		num++;
		pthread_mutex_unlock(&mutex);	// unlock the mutex
		if(num == 3){	// when the condition is met,other waiting threads are started(满足条件时,启动其他等待线程)
			pthread_cond_signal(&cond);	// trgger the waiting thread one on one(一对一触发等待线程)
			
		}
		
		sleep(1);
	}
	
		
	return NULL;	// the return value is necessary
}

/*
Thread exclusive operation.
*/
int main()
{
	int ret;
	int param = 99;
	
	pthread_t t1;		// define thread space.
	pthread_t t2;
	

	ret = pthread_create(&t1,NULL,func1,(void*)&param);	// thread-space binding to a specific function or function.
	if(ret == 0){	// if ret is 0,the binding succeeds.othrwise,the binding fails.
		printf("t1 binding succeeds.\n");
	}
	
	ret = pthread_create(&t2,NULL,func2,(void*)&param);
	if(ret == 0){
		printf("t2 binding succeeds.\n");
	}
	
	pthread_join(t1,NULL);	// the thread is blocked,and the program executes down after the thread completes
	pthread_join(t2,NULL);
	
	pthread_mutex_destroy(&mutex);	// destroy the mutex
	pthread_cond_destroy(&cond);	// conditional destruction
	return 0;
}

拓展(脚本执行)

这两句命令是在Unix/Linux shell中运行的,与可执行文件a.out和文件ret.txt有关。
让我们逐一解析它们:

./a.out 2 >ret.txt

./a.out:运行当前目录下的名为a.out的可执行文件。
1:这通常指的是文件描述符2,即标准错误(stderr)。但在这种情况下,它没有被用作重定向的一部分,
因为它前面没有重定向操作符(如>>>)。所以,这个2可能是一个参数传递给a.out的,
而不是用于重定向的。
>ret.txt:这是一个重定向操作,它将标准输出(stdout,文件描述符1)的内容重定向到文件ret.txt。
>如果ret.txt已经存在,它将被覆盖;如果不存在,它将被创建。
所以,这个命令的意思是:运行a.out并传递2作为参数,同时将其标准输出重定向到ret.txt。

./a.out 2 >>ret.txt &

./a.out:同样,运行当前目录下的名为a.out的可执行文件。
2:同样,这通常指的是文件描述符2,即标准错误(stderr)>>ret.txt:这是一个追加重定向操作,
它将标准输出(stdout)的内容追加到文件ret.txt的末尾。如果ret.txt不存在,它将被创建。
但这里有个微妙之处:由于我们想要重定向标准错误,我们应该使用2>>ret.txt而不是>>ret.txt。
但在这个命令中,我们没有为2指定重定向操作符,所以2仍然可能是一个参数。
&:这个符号使得命令在后台运行,意味着你可以继续在shell中输入其他命令,而不需要等待a.out执行完成。
但如果你想要将标准错误重定向到ret.txt并在后台运行,你应该这样写:./a.out 2>>ret.txt &
所以,这个原始命令的意思可能是:运行a.out并传递2作为参数,同时尝试将其标准输出(而不是标准错误)
追加到ret.txt中,并在后台运行该命令。但由于重定向部分可能没有正确设置,所以它可能只是将
标准输出追加到ret.txt,而标准错误仍然会显示在屏幕上。
#include <stdlib.h>

int main(int argc,char** argv)
{
	int i = 0;
	int arg = atoi(argv[1]);	// force the argv argument to be numeric(强转argv参数为数值型)
	for(i;i < arg;i++){
		system("./pth1");	// 脚本执行文件
	}
	return 0;
}

/*
	./a.out 2 >>ret.txt &
	参数1---./a.out
	参数2---2		次
	>>ret.txt	执行结果写入ret.txt文件中(追加的方式)
	&	后台运行
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值