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

线程的创建 

线程的创建是通过调用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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值