线程学习笔记

线程是进程内的一个执行单元 ,操作系统能够进行运算调动的最小单位 。一个进程里面可以有多个线程

extern int pthread_create (pthread_t *__restrict __newthread,
			   const pthread_attr_t *__restrict __attr,
			   void *(*__start_routine) (void *),
			   void *__restrict __arg) __THROWNL __nonnull ((1, 3));

pthread_t *__restrict __newthread:线程号
const pthread_attr_t *__restrict __attr:线程的定义属性,一般默认值NULL
void (__start_routine) (void *):线程接下来要执行的函数 传入的参数和返回值必须(void *)
void *__restrict __arg) __THROWNL __nonnull ((1, 3));第三个函数的传入参数,如果没有参数,可以写NULL
并发:两个或者多个活动,同时独立进行。

1 线程创建基础代码

pthread_t 实际为long类型的变量

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

#define BUF_LEN 1024

char *buf;
//读线程需要执行的代码逻辑
void *input_thread(void *argv)
{
    int i=0;
    while(1)
    {
        char c=fgetc(stdin);
        //STDIN_FILENO文件描述符
        if(c&&c!='\n')
        {
          buf[i++]=c;
        }
        //缓冲区溢出
        if(i>=BUF_LEN)
        {
            i=0;
        }
    }
}
//写线程需要执行的代码逻辑
void *output_thread(void *argv)
{
    int i=0;
    while(1)
    {
        if(buf[i])
        {
            //读取一个字节写出到控制台之后换行
            fputc(buf[i],stdout);
            fputc('\n',stdout);
            buf[i++]=0;
            //读取数据到最大下标
            if(i>BUF_LEN)
            {
                i=0;
            }
        }
        else
        {
            //如果当前没有写入数据
            sleep(1);
        }
    }
}
//程序实现创建两个线程的功能 
//1号线程读取控制台信息,写入缓存中
//2号线程将缓存信息写出到控制台

int main(int argc, char const *argv[])
{
    //初始化buf
    buf=malloc(BUF_LEN);
    //声明线程id
    pthread_t pid_input;//线程号,类似整数
    pthread_t pid_output;
    //创建读线程
    pthread_create(&pid_input,NULL,input_thread,NULL);
    //创建写线程
    pthread_create(&pid_output,NULL,output_thread,NULL);
    //线程ID 线程定制属性一般无 线程执行的函数 函数的传递参数

    //主线程等待读写线程结束
    pthread_join(pid_input,NULL);
    pthread_join(pid_output,NULL);
    free(buf);
    return 0;
}

在这里插入图片描述

2 线程终止

线程终止相关方法
(1)线程函数执行return 不推荐
(2)线程函数内部调用void pthread_exit(void *retval)函数
该函数可以通过retval向其他线程传递信息,retval指向的区域不可以放在线程函数栈内,其他线程需要这个返回值,需要调用等待方式
1、pthread_join函数
int pthread_join(pthread_t thread,void *retval)
return 成功0 失败1
join方法不需要返回结果可以填为NULL 该函数会阻塞当前线程,等到回收之后继续往下执行
2、int pthread_detach(pthread_t thread)
无需阻塞,进行标记,线程运行完之后自动回收
3、pthread_cancel
取消状态 默认启用
取消类型 默认被推迟,执行取消点函数
取消状态启用,取消类型被推迟,会立即响应取消请求并终止执行,马上终止线程号程序
返回0,代表取消请求发送
判断取消是否完成,调用pthread_join函数
(3)其他线程调用pthread_cancel函数

线程测试例程:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h> 
//定义结果结构体
typedef struct Result
{
    char *p;
    int len;
} Result;
/**
 * 红玫瑰
 * 
 * void *argv: 指针,可以传递的参数  这里用一个字符表示她的代号
 * return: void* 结果结构体指针 返回结局
 */
void *red_thread(void *argv)
{
    Result *result = malloc(sizeof(Result));
    char code = *((char *)argv);
    // 存放回信
    char *ans = malloc(101);
    while (1)
    {
        fgets(ans,100,stdin);
        if (ans[0]==code)
        {
            //接收到了对应的信息
            free(ans);
            printf("红玫瑰离开了!\n");
            char *redAns = strdup("红玫瑰独自去了纽约.\n");
            result->p = redAns;
            result->len = strlen(redAns);
            // 结束线程 输出返回值
            pthread_exit((void *)result);
        }else{
            printf("红玫瑰还在等你!\n");
        }
    }
    
}
/**
 * 白玫瑰
 * 
 * void *argv: 指针,可以传递的参数  这里用一个字符表示她的代号
 * return: void* 结果结构体指针 返回结局
 */
void *white_thread(void *argv)
{
    Result *result = malloc(sizeof(Result));
    char code = *((char *)argv);
    // 存放回信
    char *ans = malloc(101);
    while (1)
    {
        fgets(ans,100,stdin);
        if (ans[0]==code)
        {
            //接收到了对应的信息
            free(ans);
            printf("白玫瑰离开了!\n");
            char *redAns = strdup("白玫瑰独自去了伦敦.\n");
            result->p = redAns;
            result->len = strlen(redAns);
            // 结束线程 输出返回值
            pthread_exit((void *)result);
        }else{
            printf("白玫瑰还在等你!\n");
        }
    }
}
int main()
{
    pthread_t pid_red;
    pthread_t pid_white;
    char red_code='r';
    char white_code='w';
    Result *red_result = NULL;
    Result *white_result = NULL;
    // 创建红玫瑰线程
    pthread_create(&pid_red, NULL, red_thread, &red_code);
    // 创建白玫瑰线程
    pthread_create(&pid_white, NULL, white_thread, &white_code);
    // 获取红玫瑰结果
    pthread_join(pid_red, (void **)&red_result);
    printf("红玫瑰故事结局:%s\n",red_result->p);
    // 释放内存
    free(red_result->p);
    free(red_result);
    // 获取求素数结果
    pthread_join(pid_white, (void **)&white_result);
    printf("白玫瑰故事结局:%s\n",white_result->p);
    
    // 释放内存
    free(white_result->p);
    free(white_result);
    return 0;
}

3、竞态条件与线程同步

当多个线程或进程并发修改同一个共享资源,如果没有适当的同步措施,就会遇见线程或进程同步问题。这种情况下,程序的最终执行结果依赖于线程或进程执行的具体时序,导致了竞态条件。
linux系统常用于实现线程同步的三种方法:互斥锁、条件变量与信号量。

(1)互斥锁

初始化,
锁定,获取锁定,锁被其他线程持有,调用线程将被阻塞。
尝试锁定,获取锁定,锁被其他线程持有,立即返回EBUSY
解锁,用完解锁让其他线程用
销毁:清理互斥锁资源

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

#define THREAD_COUNT 20000

static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;

/**
 * @brief 对传入值累加1
 *
 * @param argv 传入指针
 * @return void* 无返回值
 */
void *add_thread(void *argv)
{
    int *p = argv;
    // 累加之前加锁,此时其他获取该锁的线程都会被阻塞
    pthread_mutex_lock(&counter_mutex);
    (*p)++;
    // 累加之后释放锁片
       pthread_mutex_unlock(&counter_mutex);
    return (void *)0;
}

int main()
{
    pthread_t pid[THREAD_COUNT];

    int num = 0;

    // 用20000个线程对num作累加
    for (int i = 0; i < THREAD_COUNT; i++)
    {
        pthread_create(pid + i, NULL, add_thread, &num);
    }

    // 等带所有线程结束
    for (int i = 0; i < THREAD_COUNT; i++)
    {
        pthread_join(pid[i], NULL);
    }

    // 打印累加结果
    printf("累加结果:%d\n", num);

    return 0;
}

结果稳定为20000
进程结束时,操作系统会自动回收该进程的所有资源,包括内存,打开的文件描述符和互斥锁等。显式销毁不是必需的
对于静态初始化,并且在程序结束时不再被使用的互斥锁(上述程序中的counter_mutex),显式销毁不是必需的
若互斥锁动态分配(使用pthread_mutex_init函数初始化),或者互斥锁会被跨多个函数或文件使用,不再需要时必须显式销毁它

(2)读写锁

读操作:在读写锁的控制下,多个线程可以同时获得读锁,这些线程可以并发的读取共享资源,但是他们的存在阻止了写锁的授权
写操作:如果至少有一个读操作持有锁,写操作就无法获得锁,写操作会被阻塞,直到所有读锁被释放。
可以设置锁属性,定义为写优先,避免发生·写饥饿

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

pthread_rwlock_t rwlock;
int shared_data = 0;

void *lock_reader(void *argv) {
    pthread_rwlock_rdlock(&rwlock);
    printf("this is %s, value is %d\n", (char *)argv, shared_data);

    pthread_rwlock_unlock(&rwlock);
}

void *lock_writer(void *argv) {
    pthread_rwlock_wrlock(&rwlock);
    int tmp = shared_data + 1;
    sleep(1);
    shared_data = tmp;
    printf("this is %s, shared_data++\n", (char *)argv);
      pthread_rwlock_unlock(&rwlock);
}

int main() {
    pthread_rwlock_init(&rwlock, NULL);

    pthread_t writer1, writer2, reader1, reader2, reader3, reader4, reader5, reader6;
    pthread_create(&writer1, NULL, lock_writer, "writer1");
    pthread_create(&writer2, NULL, lock_writer, "writer2");
    sleep(3);
    pthread_create(&reader1, NULL, lock_reader, "reader1");
    pthread_create(&reader2, NULL, lock_reader, "reader2");
    pthread_create(&reader3, NULL, lock_reader, "reader3");
    pthread_create(&reader4, NULL, lock_reader, "reader4");
    pthread_create(&reader5, NULL, lock_reader, "reader5");
    pthread_create(&reader6, NULL, lock_reader, "reader6");

    pthread_join(writer1, NULL);
    pthread_join(writer2, NULL);
    pthread_join(reader1, NULL);
    pthread_join(reader2, NULL);
    pthread_join(reader3, NULL);
    pthread_join(reader4, NULL);
    pthread_join(reader5, NULL);
    pthread_join(reader6, NULL);

    pthread_rwlock_destroy(&rwlock);
}

写饥饿
① 读操作可以并发执行,相互之间不必争抢锁,多个读操作可以同时获得读锁;② 只要有一个线程持有读写锁,写操作就会被阻塞。我们在读操作中加1s休眠,只要有一个读线程获得锁,在1s内写操作是无法执行的,其它读操作就可以有充足的时间执行,因此读操作就会连续发生,写操作必须等待所有读操作执行完毕方可获得读写锁执行写操作。这就是使用读写锁时存在的潜在问题:写饥饿。

(3)自旋锁 一般不用

在Linux内核中,自旋锁是一种用于多处理器系统中的低级同步机制,主要用于保护非常短的代码段或数据结构,以避免多个处理器同时访问共享资源。自旋锁相对于其他锁的优点是它们在锁被占用时会持续检查锁的状态(即“自旋”),而不是让线程进入休眠。这使得自旋锁在等待时间非常短的情况下非常有效,因为它避免了线程上下文切换的开销。
自旋锁主要用于内核模块或驱动程序中,避免上下文切换的开销。不能在用户空间使用。

(4)条件变量

restrict关键字修饰指针,告诉编辑器被修饰的指针式编辑器所知的唯一一个在其作用域内用来访问指针所指向的对象的方法。
函数参数使用restrict修饰,相当于约定:函数执行期间,该参数指向的内存区域不会被其它指针修改。
pthread_cond_t是一个条件变量,它是线程间同步的另一种机制。与pthread_mutex_t相同,它也定义在头文件<pthreadtypes.h>中。

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

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int count = 0;
// 初始化锁
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 初始化条件变量
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 生产者线程
void *producer(void *arg)
{
    int item = 1;
     // 获取互斥锁 拿不到等
        pthread_mutex_lock(&mutex);
    while (1)
    {
        // 如果缓冲区满,等消费者读取
        while (count == BUFFER_SIZE)
        {
            // 暂停线程 等待唤醒
            //阻塞并临时释放互斥锁
            pthread_cond_wait(&cond, &mutex);
        }
        // 能到这里说明缓冲区不满了 可以写一个
        buffer[count++] = item++;
        printf("白月光发送一个幸运数字%d\n", buffer[count - 1]);
        // 通知消费者可以消费数据了
        // 唤醒消费者 同时解锁
        pthread_cond_signal(&cond);
    }
    pthread_mutex_unlock(&mutex);//不会执行
}
void *consumer(void *arg)
{
 // 获取互斥锁 拿不到等
        pthread_mutex_lock(&mutex);
    while (1)
     {
       
        // 如果缓冲区为空,则等待生产者生产数据
        while (count == 0)
        {
            // 暂停线程 等待唤醒
            pthread_cond_wait(&cond, &mutex);
        }
        printf("我收到了幸运数字 %d\n", buffer[--count]);
        // 通知生产者可以发送数据了
        // 唤醒生产者 同时解锁
        pthread_cond_signal(&cond);
    }
            pthread_mutex_unlock(&mutex);
}
int main()
{
    pthread_t producer_thread, consumer_thread;
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
    return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count = 0;

void *decrement_count(void *arg)
{
    pthread_mutex_lock(&count_lock);
    printf("decrement_count get count_lock\n");
    while(count == 0)
    {
        printf("decrement_count count == 0 \n");
        printf("decrement_count before cond_wait\n");
        pthread_cond_wait(&count_nonzero, &count_lock);
        printf("decrement_count after cond_wait\n");
    }

    count = count + 1;
    pthread_mutex_unlock(&count_lock);
}

void *increment_count(void *arg)
{
    pthread_mutex_lock(&count_lock);
    printf("increment_count get count_lock\n");
    if(count == 0)
    {
        printf("increment_count before cond_signal\n");
        pthread_cond_signal(&count_nonzero);
        printf("increment_count after cond_signal\n");
    }

    count = count + 1;
    pthread_mutex_unlock(&count_lock);
}

int main(void)
{
    pthread_t tid1, tid2;

    pthread_mutex_init(&count_lock, NULL);
    pthread_cond_init(&count_nonzero, NULL);

    pthread_create(&tid1, NULL, decrement_count, NULL);
    sleep(2);
    pthread_create(&tid2, NULL, increment_count, NULL);

    sleep(10);
    printf("%d\n",count);
    

    return 0;
}

运行结果:
在这里插入图片描述

(5)信号量

信号量(Semaphore)是一种广泛使用的同步机制,用于控制对共享资源的访问,主要在操作系统和并发编程领域中得到应用,用来解决多个进程或线程间的同步与互斥问题。
非负整数变量,主要作用:互斥和同步。互斥,多个进程或者线程不会同时访问共享资源。同步,不同进程和线程按照期望的顺序执行。
存在竞态条件的测试例程

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

int shard_num = 0;

void *plusOne(void *argv) {
    int tmp = shard_num + 1;
    shard_num = tmp;
}

int main() {
    pthread_t tid[10000];
    for (int i = 0; i < 10000; i++) {
        pthread_create(tid + i, NULL, plusOne, NULL);
    }

    for (int i = 0; i < 10000; i++) {
        pthread_join(tid[i], NULL);
    }
    
    printf("shard_num is %d\n", shard_num);

    return 0;
}

解决了竞态条件的测试例程

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

sem_t unnamed_sem;
int shard_num = 0;

void *plusOne(void *argv) {
    sem_wait(&unnamed_sem);
    int tmp = shard_num + 1;
    shard_num = tmp;
    sem_post(&unnamed_sem);
}

int main() {
    sem_init(&unnamed_sem, 0, 1);

    pthread_t tid[10000];
      for (int i = 0; i < 10000; i++) {
        pthread_create(tid + i, NULL, plusOne, NULL);
    }

    for (int i = 0; i < 10000; i++) {
        pthread_join(tid[i], NULL);
    }
    
    printf("shard_num is %d\n", shard_num);

    sem_destroy(&unnamed_sem);

    return 0;
}

结果稳定在10000
作为二进制信号量用于进程之间通信(线程比进程之间的资源共享程度更高,用于进程之间通信的方式,通常也能用于线程之间进行通信)
(1)存在竞态条件的例程

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    char *shm_value_name = "unnamed_sem_shm_value";
    
    // 创建内存共享对象
    int value_fd = shm_open(shm_value_name, O_CREAT | O_RDWR, 0666);
    // 调整内存共享对象的大小
    ftruncate(value_fd, sizeof(int));
    // 将内存共享对象映射到共享内存区域
    int *value = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, value_fd, 0);

    // 初始化共享变量的值
    *value = 0;

    int pid = fork();//对于父进程而言,返回的是子进程的PID,对于子进程而言,返回值为0

    if (pid > 0) {
        int tmp = *value + 1;
        sleep(1);
        *value = tmp;
        
        // 等待子进程执行完毕
        waitpid(pid, NULL, 0);
        printf("this is father, child finished\n");
        printf("the final value is %d\n", *value);
} else if (pid == 0) {
        int tmp = *value + 1;
        sleep(1);
        *value = tmp;
} else {
        perror("fork");
    }

    // 无论父子进程都应该解除共享内存的映射,并关闭共享对象的文件描述符
    if (munmap(value, sizeof(int)) == -1)
    {
        perror("munmap value");
    }

    if (close(value_fd) == -1)
    {
        perror("close value");
    }

    // 如果调用时别的进程仍在使用共享对象,则等待所有进程释放资源后,才会销毁相关资源。
    // shm_unlink只能调用一次,这里在父进程中调用shm_unlink
    if (pid > 0)
    {
        if (shm_unlink(shm_value_name) == -1)
        {
            perror("father shm_unlink shm_value_name");
        }
    }

    return 0;
}

解决了竞态条件的例程

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    char *shm_sem_name = "unnamed_sem_shm_sem";
    char *shm_value_name = "unnamed_sem_shm_value";

    // 创建内存共享对象
    int sem_fd = shm_open(shm_sem_name, O_CREAT | O_RDWR, 0666);
    int value_fd = shm_open(shm_value_name, O_CREAT | O_RDWR, 0666);

    // 调整内存共享对象的大小
    ftruncate(sem_fd, sizeof(sem_t));
     ftruncate(value_fd, sizeof(int));

    // 将内存共享对象映射到共享内存区域
    sem_t *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, sem_fd, 0);
    
    int *value = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, value_fd, 0);
    
    // 初始化信号量和共享变量的值
    sem_init(sem, 1, 1);
    *value = 0;

    int pid = fork();

    if (pid > 0)
    {
        sem_wait(sem);
        int tmp = *value + 1;
        sleep(1);
        *value = tmp;
        sem_post(sem);

        // 等待子进程执行完毕
        waitpid(pid, NULL, 0);
        printf("this is father, child finished\n");
        printf("the final value is %d\n", *value);
    }
    else if (pid == 0)
    {
        sem_wait(sem);
        int tmp = *value + 1;
        sleep(1);
        *value = tmp;
        sem_post(sem);
    }
    else
    {
        perror("fork");
    }

    // 父进程执行到这里,子进程已执行完毕,可以销毁信号量
    if (pid > 0)
    {
      if (sem_destroy(sem) == -1)
        {
            perror("sem_destory");
        }
    }

    // 无论父子进程都应该解除共享内存的映射,并关闭共享对象的文件描述符
    if (munmap(sem, sizeof(sem)) == -1)
    {
        perror("munmap sem");
    }

    if (munmap(value, sizeof(int)) == -1)
    {
        perror("munmap value");
    }

    if (close(sem_fd) == -1)
    {
        perror("close sem");
    }

    if (close(value_fd) == -1)
    {
        perror("close value");
    }

    // 如果调用时别的进程仍在使用共享对象,则等待所有进程释放资源后,才会销毁相关资源。
    // shm_unlink只能调用一次,这里在父进程中调用shm_unlink
    if (pid > 0)
    {
        if (shm_unlink(shm_sem_name) == -1)
        {
            perror("father shm_unlink shm_sem_name");
        }

        if (shm_unlink(shm_value_name) == -1)
        {
            perror("father shm_unlink shm_value_name");
        }
    }

    return 0;
    }

运行结果稳定为2.
作为计数信号量用于线程之间的通信

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

sem_t *full;
sem_t *empty;

int shard_num;

int rand_num()
{
    srand(time(NULL));

    return rand();
}

void *producer(void *argv)
{
    for (int i = 0; i < 5; i++)
    {
        sem_wait(empty);
        printf("\n==========> 第 %d 轮数据传输 <=========\n\n", i + 1);
        sleep(1);
        shard_num = rand_num();
        printf("producer has sent data\n");
        sem_post(full);
    }
}

void *consumer(void *argv)
{
    for (int i = 0; i < 5; i++)
    {
        sem_wait(full);
        printf("consumer has read data\n");
        printf("the shard_num is %d\n", shard_num);
        sleep(1);
        sem_post(empty);
    }
}

int main()
{
    full = malloc(sizeof(sem_t));
       empty = malloc(sizeof(sem_t));

    sem_init(empty, 0, 1);
    sem_init(full, 0, 0);

    pthread_t producer_id, consumer_id;
    pthread_create(&producer_id, NULL, producer, NULL);
    pthread_create(&consumer_id, NULL, consumer, NULL);

    pthread_join(producer_id, NULL);
    pthread_join(consumer_id, NULL);

    sem_destroy(empty);
    sem_destroy(full);

    return 0;
}

作为计数信号量用于进程之间的通信

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    char *shm_name = "unnamed_sem_shm";

    // 创建内存共享对象
    int fd = shm_open(shm_name, O_CREAT | O_RDWR, 0666);
    // 调整内存共享对象的大小
    ftruncate(fd, sizeof(sem_t));
    // 将内存共享对象映射到共享内存区域
    sem_t *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    // 初始化信号量
    sem_init(sem, 1, 0);

    int pid = fork();

    if (pid > 0)
    {
        sem_wait(sem);
        printf("this is father\n");
        // 父进程等待子进程退出并回收资源
        waitpid(pid, NULL, 0);
    }
    else if (pid == 0)
    {
        sleep(1);
        printf("this is son\n");
        sem_post(sem);
    }
    else
    {
        perror("fork");
    }

    // 父进程执行到此处,子进程已执行完毕,可以销毁信号量
    // 子进程执行到此处,父进程仍在等待信号量,此时销毁会导致未定义行为
    // 只有父进程中应该销毁信号量
    if (pid > 0)
    {
      if (sem_destroy(sem) == -1)
        {
            perror("father sem_destroy");
        }
    }

    // 父子进程都应该解除映射,关闭文件描述符
    if (munmap(sem, sizeof(sem)) == -1)
    {
        perror("munmap");
    }
    if (close(fd) == -1)
    {
        perror("close");
    }

    // shm_unlink只能调用一次,只在父进程中调用
    if (pid > 0)
    {
        if (shm_unlink(shm_name) == -1)
        {
            perror("father shm_unlink");
        }
    }

    return 0;
}

有名信号量
有名信号量作为二进制信号量

#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    char *sem_name = "/named_sem";
    char *shm_name = "/named_sem_shm";

    // 初始化有名信号量
    sem_t *sem = sem_open(sem_name, O_CREAT, 0666, 1);

    // 初始化内存共享对象
    int fd = shm_open(shm_name, O_CREAT | O_RDWR, 0666);
    // 调整内存共享对象的大小
    ftruncate(fd, sizeof(int));
    // 将内存共享对象映射到内存空间
    int *value = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    // 初始化共享变量指针指向位置的值
    *value = 0;

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork");
    }

    sem_wait(sem);
    int tmp = *value + 1;
    sleep(1);
    *value = tmp;
    sem_post(sem);

    // 每个进程都应该在使用完毕后关闭对信号量的连接
    sem_close(sem);
    if (pid > 0)
    {
        waitpid(pid, NULL, 0);
               printf("子进程执行结束,value = %d\n", *value);

        // 有名信号量的取消链接只能执行一次
        sem_unlink(sem_name);
    }

    // 父子进程都解除内存共享对象的映射,并关闭相应的文件描述符
    munmap(value, sizeof(int));
    close(fd);

    // 只有父进程应该释放内存共享对象
    if (pid > 0)
    {
        if (shm_unlink(shm_name) == -1)
        {
            perror("shm_unlink");
        }
    }

    return 0;
}

有名信号量用作计数信号量

#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    char *sem_name = "/named_sem";

    // 初始化有名信号量
    sem_t *sem = sem_open(sem_name, O_CREAT, 0666, 0);

    pid_t pid = fork();
    if (pid > 0) {
        sem_wait(sem);
        printf("this is father\n");
        // 等待子进程执行完毕
          waitpid(pid, NULL, 0);
        
        // 释放引用
        sem_close(sem);

        // 释放有名信号量
        if(sem_unlink(sem_name) == -1) {
            perror("sem_unlink");
        }
    } else if(pid == 0) {
        sleep(1);
        printf("this is son\n");
        sem_post(sem);

        // 释放引用
        sem_close(sem);
    } else
    {
        perror("fork");
    }

    return 0;
}

线程池测试例程

#include <glib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
// 任务函数
void task_func(gpointer data, gpointer user_data) {
    int task_num = *(int*)data;
    free(data);
    printf("Executing task is %d...\n", task_num);
    sleep(1);
    printf("Task %d completed\n", task_num);
}

int main() {
    // 创建线程池
    GThreadPool *thread_pool = g_thread_pool_new(task_func, NULL, 5, TRUE, NULL);//gpointer user_data

    // 向线程池添加任务
    for (int i = 0; i < 10; i++) {
        int *tmp = malloc(sizeof(int));
        *tmp = i + 1;
        g_thread_pool_push(thread_pool, tmp, NULL);//gpointer data
    }
    // 等待所有任务完成
    g_thread_pool_free(thread_pool, FALSE, TRUE);
    printf("All tasks completed\n");

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值