首页 > 其他分享 >线程:线程创建pthread_create,线程间的同步与互斥

线程:线程创建pthread_create,线程间的同步与互斥

时间:2024-08-27 20:57:35浏览次数:8  
标签:create 互斥 线程 pthread mutex sem NULL void

线程的创建 

线程的创建是通过调用pthread_create函数来实现的。该函数的原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

参数说明:

  • thread:指向pthread_t类型的指针,用于存储新线程的ID。
  • attr:指向pthread_attr_t类型的指针,用于指定新线程的属性,可以为NULL。
  • start_routine:指向线程函数的指针,该函数的返回类型为void*,参数类型为void*。
  • arg:传递给线程函数的参数。

函数返回值:

  • 若线程创建成功,则返回0。
  • 若线程创建失败,则返回一个非零的错误码。

线程的退出 

退出线程:线程的退出是通过调用pthread_exit函数来实现的。该函数的原型如下:

void pthread_exit(void *value_ptr);

参数说明:

  • retval:指定的退出码。

使用该函数可以在不终止整个进程的情况下结束当前线程,并将退出码返回给等待它的线程。

该函数使当前线程退出,并将value_ptr指向的值作为退出状态返回给其他线程。可以将value_ptr参数设置为NULL,以表示退出状态不重要。线程在执行pthread_exit函数后,其资源会被系统自动回收。

线程的回收 

回收线程:线程的回收是通过调用pthread_join函数来实现的,pthread_join是一个函数,用于等待指定的线程终止。该函数的原型如下:

int pthread_join(pthread_t thread, void **value_ptr);

参数thread是要等待的线程的标识符,value_ptr是一个指向指针的指针,用于存储线程的返回值。

调用pthread_join函数将会阻塞调用线程,直到指定的线程终止。如果线程已经终止,pthread_join函数立即返回。如果线程还在运行,则调用线程将会等待,直到指定的线程终止。

如果线程的返回值不为NULL,则它将被存储在value_ptr指向的位置上。如果不关心线程的返回值,则可以将value_ptr参数设置为NULL。

pthread_join函数返回0表示成功,非零值表示失败。常见的失败情况包括:线程标识符无效或者线程已经被其他线程等待。

注意:在线程退出后,如果不进行回收操作,线程的相关资源可能会得不到释放,从而导致资源泄露。因此,建议在创建线程后,及时进行线程的回收操作。

线程的同步与互斥

1.互斥锁

在C语言中,使用线程互斥锁(Mutex)可以实现对共享资源的互斥访问,避免多个线程同时访问产生的竞争条件。下面是使用线程互斥锁的基本步骤:

  1. 包含头文件 pthread.h
#include <pthread.h>
  1. 定义一个互斥锁变量。
pthread_mutex_t mutex;
  1. 在需要保护的代码片段前后使用锁来进行加锁和解锁。
// 加锁
pthread_mutex_lock(&mutex);

// 保护的代码片段

// 解锁
pthread_mutex_unlock(&mutex);
  1. 在每个线程的入口函数中初始化互斥锁。
void* thread_func(void* arg) {
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 其他操作
    
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);

    return NULL;
}

需要注意的是,互斥锁的初始化和销毁只需要在程序的开始和结束阶段进行一次。在每次加锁和解锁操作前后,确保使用同一把互斥锁进行操作,以保证正确性。

另外,需要注意互斥锁只能保证同一进程内的线程互斥访问,对于不同进程间的线程需要使用其他的同步机制,如信号量或文件锁等。

2.线程同步之无名信号量

无名信号量是一种线程同步的机制,用于协调多个线程之间的执行顺序。在C语言中,可以使用pthread库中的信号量函数来实现无名信号量。

无名信号量包括两个主要的操作:P操作和V操作。P操作用于申请资源,V操作用于释放资源。当一个线程需要访问共享资源时,需要执行P操作来申请资源;当一个线程使用完共享资源后,需要执行V操作来释放资源。

3.线程同步之条件变量

在C语言中,条件变量是一种线程同步机制,它允许一个线程(或多个线程)等待另一个线程满足特定的条件后再继续执行。

条件变量通常与互斥量(mutex)一起使用,以确保在访问共享资源之前,只有一个线程能够进入关键区域。下面是使用条件变量的一般步骤:

  1. 定义条件变量和互斥量
pthread_cond_t cond;
pthread_mutex_t mutex;
  1. 初始化条件变量和互斥量
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
  1. 线程等待条件变量满足
pthread_mutex_lock(&mutex);
while (condition_not_met) {
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
  1. 满足条件后,通知等待的线程
pthread_mutex_lock(&mutex);
set_condition_true();
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);

在步骤3中,线程会进入等待状态,并释放互斥量。当条件变量满足时,其他线程可以通过调用pthread_cond_signal或pthread_cond_broadcast来通知等待的线程。

需要注意的是,条件变量的使用必须与互斥量配合使用,这是为了避免竞态条件(race condition)。在等待条件变量时,线程会自动释放互斥量,并在接收到通知后重新获取互斥量。

总的来说,条件变量是一种用于线程同步的机制,它提供了一种等待特定条件满足的方式,并在条件满足时通知等待的线程。

作业1:创建3个线程,一个子线程拷贝文件的前一半,一个子线程拷贝后一半文件,主线程回收子线程资源。

#include <myhead.h>

sem_t sem1,sem2,sem3;

pthread_t tid1,tid2,tid3;

int up_text()
{
 
	int fd1,fd2;
	char buff[1024];
	int len;
	fd1=open("./1.txt",O_RDONLY);
	if(fd1==-1)
	{
		perror("open");
		return -1;
	}
 
	len=lseek(fd1,0,SEEK_END);
	if(len==-1)
	{
		printf("拷贝失败\n");
		close(fd1);
	}
	lseek(fd1,0,SEEK_SET);
 
	read(fd1,buff,len/2);//读取1.txt上半内容,存入buff
 
	fd2=open("./2.txt",O_WRONLY | O_CREAT ,0664);
	if(fd2==-1)
	{
		perror("open");
		close(fd2);
		return -1;
	}
 
	write(fd2,buff,len/2);//将buff的内容写入2.txt
	printf("上文拷贝成功\n");
 
	close(fd1);
	close(fd2);
 
}
 
int down_text()
{
	int fd1,fd2;
	char buff[1024];
	int len;
	fd1=open("./1.txt",O_RDONLY);
	if(fd1==-1)
	{
		perror("open");
		return -1;
	}
 
	len=lseek(fd1,0,SEEK_END);
	if(len==-1)
	{
		printf("拷贝失败\n");
	}
	lseek(fd1,len/2,SEEK_SET);
 
	read(fd1,buff,len/2);//读取1.txt 下半内容,存入buff
 
	fd2=open("./2.txt",O_WRONLY | O_CREAT ,0664);
	if(fd2==-1)
	{
		perror("open");
		return -1;
	}
 
	lseek(fd2,len/2,SEEK_SET);
 
	write(fd2,buff,len/2);//将buff的内容写入2.txt
	printf("下文拷贝成功\n");
 
	close(fd1);
	close(fd2);
 
}

void *fun1(void *ggg)//主线程
{
   	sem_wait(&sem1);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
	sem_post(&sem3);
    pthread_exit(NULL);

}
void *fun2(void *ggg)//子线程:复制前一半
{
	sem_wait(&sem3);
	up_text();
   	sem_post(&sem2);
    pthread_exit(NULL);
}
void *fun3(void *ggg)//子线程:复制后一半
{
	sem_wait(&sem2);
	down_text();
 	sem_post(&sem1);
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    
	sem_init(&sem1,0,0);
    sem_init(&sem2,0,0);
    sem_init(&sem3,0,1);

    if(pthread_create(&tid1,NULL,fun1,NULL)!=0)
    {
        perror("ptcreat1");
        return -1;
    }
    if(pthread_create(&tid2,NULL,fun2,NULL)!=0)
    {
        perror("ptcreat2");
        return -1;
    }
    if(pthread_create(&tid3,NULL,fun3,NULL)!=0)
    {
        perror("ptcreat3");
        return -1;
    }

    sem_destroy(&sem1);
 	sem_destroy(&sem2);
	sem_destroy(&sem3);   
    pthread_join(tid1,NULL);
    return 0;
}

作业二:使用无名信号量实现循环输出 春、夏、秋、冬。

#include <myhead.h>
//春夏秋冬
sem_t sem1,sem2,sem3,sem4;
void *fun1(void *ggg)
{
    while(1)
    {
        sem_wait(&sem4);//申请将sem3的 value-=1
        printf("春\t");
        fflush(stdout);
        sem_post(&sem1);//释放 sem2 的 value=1
    }
    pthread_exit(NULL);
}
void *fun2(void *ggg)
{
    while(1)
    {
        sem_wait(&sem1);//将sem2的value=0
        printf("夏\t");
        fflush(stdout);
        sem_post(&sem2);//释放sem1 value=1
    }
    pthread_exit(NULL);
}
void *fun3(void *ggg)
{
    while(1)
    {
        sem_wait(&sem2);
        printf("秋\t");
        fflush(stdout);
        sem_post(&sem3);
    }
    pthread_exit(NULL);
}
void *fun4(void *ggg)
{
    while(1)
    {
        sem_wait(&sem3);
        printf("冬\t");
        fflush(stdout);
        sem_post(&sem4);
    }
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    pthread_t tid1,tid2,tid3,tid4;
    
    sem_init(&sem1,0,0);
    sem_init(&sem2,0,0);
    sem_init(&sem3,0,0);
    sem_init(&sem4,0,1);//信号量sem4给进程1资源
    if(pthread_create(&tid1,NULL,fun1,NULL)!=0)
    {
        perror("ptcreat1");
        return -1;
    }
    if(pthread_create(&tid2,NULL,fun2,NULL)!=0)
    {
        perror("ptcreat2");
        return -1;
    }
    if(pthread_create(&tid3,NULL,fun3,NULL)!=0)
    {
        perror("ptcreat3");
        return -1;
    }
    if(pthread_create(&tid4,NULL,fun4,NULL)!=0)
    {
        perror("ptcreat4");
        return -1;
    }
	sem_destroy(&sem1);
	sem_destroy(&sem2);
	sem_destroy(&sem3);          
	sem_destroy(&sem4);          
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

作业三:互斥锁,无名信号量,条件变量再联系一遍。

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

// 互斥锁
pthread_mutex_t mutex;
// 条件变量
pthread_cond_t cond;
// 无名信号量
sem_t sem;

void* thread_func1(void* arg) {
    // 等待信号量
    sem_wait(&sem);
    pthread_mutex_lock(&mutex);
    printf("线程1获取了互斥锁\n");
    // 发送条件信号
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
	return NULL;
}

void* thread_func2(void* arg) {
    pthread_mutex_lock(&mutex);
    // 等待条件变量
    pthread_cond_wait(&cond, &mutex);
    printf("线程2获取了条件变量信号\n");
    pthread_mutex_unlock(&mutex);
    // 释放信号量
    sem_post(&sem);
	pthread_exit(NULL);
    return NULL;
}

int main() 
{
    pthread_t thread1, thread2;

    // 初始化互斥锁、条件变量和信号量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    sem_init(&sem, 0, 1);

    // 创建线程
    pthread_create(&thread1, NULL, thread_func1, NULL);
    pthread_create(&thread2, NULL, thread_func2, NULL);

    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 销毁互斥锁、条件变量和信号量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    sem_destroy(&sem);

    return 0;
}

标签:create,互斥,线程,pthread,mutex,sem,NULL,void
From: https://blog.csdn.net/m0_58572142/article/details/141611918

相关文章

  • Android开发 - synchronized 关键字控制多个线程对共享资源的访问解析
    什么是synchronizedsynchronized一个关键字,用于实现线程同步。其主要作用是控制多个线程对共享资源的访问,确保被synchronized修饰的代码块或方法同一时间只有一个线程可以执行,从而避免数据不一致的问题为什么需要synchronized在多线程编程中,多个线程可能同时访问和修改......
  • 使用 Callable 和 FutureTask 创建线程
    转自:http://t.csdnimg.cn/jl1ZN原文链接:https://blog.csdn.net/wzhy2016/article/details/131178612 操作流程:   创建Callable实现类的实例,并实现call方法。   使用FutureTask类来包装Callable对象(第一步创建实现类的实例)。   使用FutureTask对象作为Thread对象的......
  • 线程中的虚假唤醒
    理论基础:线程间通信:1、生产者+消费者2、通知等待唤醒机制wait和notify为什么一个关于线程的操作,方法却放在Object包下?因为多线程的线程安全,必定依赖于锁,而任何对象都可以当锁对象,所以将公共的方法放入到Object类中。多线程编程模板B:判断干活通知基于以上理论,我们......
  • 多线程-interrupt
    多线程-interrupt中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。1、案例一packagecom.example.one;/***@authortom*/publicclassMain{publicstatic......
  • python aiohttp创建很多线程的问题及解决例子解析
    在使用aiohttp进行异步HTTP请求时,创建大量线程可能会导致性能问题。根据搜索结果,这个问题通常与DNS查询有关,因为默认情况下,每次发送请求时aiohttp.ClientSession都会进行DNS查询,这是一个阻塞操作,会为每次查询创建一个新线程。为了解决这个问题,可以通过指定一个AsyncR......
  • C#上位机开发——多线程启动停止暂停继续
    引用:上位机开发——多线程启动停止暂停继续-哔哩哔哩(bilibili.com)前言初学者学习编程时,很容易因为多线程出现各种问题,导致不敢使用多线程。但是多线程技术在做开发中,是不可忽视的一个技术,基本上我们实际应用中,每个项目都会使用多线程,所以多线程技术必须掌握。为什么要用多......
  • Java线程的实践及原理揭秘
    Java线程的实践及原理揭秘并发是什么?系统支持高并发的因素是哪些?1.如何理解系统的并发一般来说,系统在单位时间内能够承载的并发数就是整个系统同事能够处理的请求数量。对于并发的指标通常通过TPS/QPS来表示QPS:每秒处理的查询数(Queries-Per-Second)TPS:每秒处理的事务数(Tr......
  • java 线程
    1.Java中有哪几种方式来创建线程执行任务1.继承Thread类(单继承)2.Runnable接口(没有继承限制)但是无法返回值3.callable接口结合FutureTask4.利用线程池来创建线程使用ExecutorService调用execute通过runnable创建底层都是基于runnable2.为什么不建议使用Executors来创建......
  • 操作系统终止线程
    终止线程方法1:从线程入口函数中return,主线程除外。方法2:调用pthread_exit函数。voidpthread_exit(void*retval);retval-和线程过程函数的返回值语义相同。注意:在任何线程中调用exit函数都将终止整个进程。问题:主线程结束,子线程是否会跟着一起结束?主线程结束,并不会......
  • 一次性下发100w的优惠券/短信/二维码,兼顾线程池参数可配置 在Spring 中 ThreadPoolTas
    一次性下发100w的优惠券/短信/二维码,兼顾线程池参数可配置在Spring中ThreadPoolTaskExecutor的使用1、场景需求分析针对6.18,11.11这种场景,平台一次性发布500w张优惠券,或者对于锁单用户统一发下100w张确认信息,同时我们平时有抢购茅台的场景,京东一次性发布10w个验证码,主要是针......