线程的创建
线程的创建是通过调用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)可以实现对共享资源的互斥访问,避免多个线程同时访问产生的竞争条件。下面是使用线程互斥锁的基本步骤:
- 包含头文件
pthread.h
。
#include <pthread.h>
- 定义一个互斥锁变量。
pthread_mutex_t mutex;
- 在需要保护的代码片段前后使用锁来进行加锁和解锁。
// 加锁
pthread_mutex_lock(&mutex);
// 保护的代码片段
// 解锁
pthread_mutex_unlock(&mutex);
- 在每个线程的入口函数中初始化互斥锁。
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)一起使用,以确保在访问共享资源之前,只有一个线程能够进入关键区域。下面是使用条件变量的一般步骤:
- 定义条件变量和互斥量
pthread_cond_t cond;
pthread_mutex_t mutex;
- 初始化条件变量和互斥量
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
- 线程等待条件变量满足
pthread_mutex_lock(&mutex);
while (condition_not_met) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
- 满足条件后,通知等待的线程
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;
}