第十二章 POSIX 线程(二)

信号量

      信号量是一个特殊类型的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作,即使在一个多线程程序中也是如此。这意味着若果一个程序中有两个(或更多)的线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。但如果是普通变量,来自同一程序中的不同线程的冲突操作导致的结果将是不确定的。

       信号量--二进制信号量,它只有 0 和 1 两种取值。还有一种更通用的信号量--计数信号量,它可以有更大的取值范围。信号量一般常用来保护一段代码,使其每次只能被一个执行线程运行,要完成这个工作。就要使用二进制信号量。由于计数信号量并不常用。所以我们在这里不对它进行深入介绍,实际上它仅仅是二进制信号量的一种逻辑扩展,两者实际调用的函数都一样。

信号量通过 sem_init 函数创建,它的定义如下:

#include <semaphore.h>

int sem_init ( sem_t *sem, int pshared, unsigned int value);

       这个函数初始化有 sem 指向的信号量对象,设置它的共享选项,并给它一个初始的整数值。pshared 参数控制信号量的类型,如果其值为 0 ,就表示这个信号量是当前进程的

局部信号量,否则,这个信号量就可以在多个进程之间共享。

接下来的两个函数控制信号量的值,它们的定义如下:

#include <semaphore.h>

int sem_wait (sem_t *sem);

int sem_post (sem_t *sem);

       这两个函数都以一个指针为参数,该指针指向的对象是由 sem_init 调用初始化的信号量。

       sem_post 函数的作用是以原子操作的方式给信号量的值加 1 。所谓原子操作是指,如果两个线程企图同时给一个信号量加 1 ,它们之间不会互相干扰,而不像如果两个程序同时对一个文件进行读取、增加、写入操作时可能会引起冲突。信号量的值总是会被正确的加 2,因为有两个线程试图改变它。

        sem_wait 函数以原子操作的方式将信号量减 1,但它会等待直到信号量有个非零值才会开始减法操作。因此,若果对值为 2 的信号量调用 sem_wait,线程将继续执行,但信号量的值会减到 1.如果对值为 0 的信号量调用 sem_wait ,这个函数就会等待,直到有其它线程增加了该信号量的值使其不再为 0 为止。如果两个线程同时在 sem_wait 调用上等待同一个信号量变为非零值,那么该信号量被被第三个线程增加 1 时,只有其中一个等待线程将开始对信号量减一,然后继续执行,另外一个线程还将继续等待。信号量的这种“在单个函数中就能原子化地进行测试和设置”的能力使其变得非常有价值。

        还有另外一个信号量函数 sem_trywait ,它是 sem_wait 的非阻塞版本。

sem_destroy ,这个函数的作用是,用完信号量后对它进行清理。它的定义如下:

#include <semaphore.h>

int sem_destroy (sem_t *sem);

       这个函数也以一个信号量指针为参数,并清理该信号量拥有的所有资源。如果企图清理的信号量正被一些线程等待,就会收到一个错误,成功时返回 0.

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

void *thread_function(void *arg);
sem_t bin_sem;

#define WORK_SIZE 1024
char work_area[WORK_SIZE];

int main() {
    int res;
    pthread_t a_thread;
    void *thread_result;

    res = sem_init(&bin_sem, 0, 0);
    if (res != 0) {
        perror("Semaphore initialization failed");
        exit(EXIT_FAILURE);
    }
    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if (res != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    printf("Input some text. Enter 'end' to finish\n");
    while(strncmp("end", work_area, 3) != 0) {
        fgets(work_area, WORK_SIZE, stdin);
        sem_post(&bin_sem);
    }
    printf("\nWaiting for thread to finish...\n");
    res = pthread_join(a_thread, &thread_result);
    if (res != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    printf("Thread joined\n");
    sem_destroy(&bin_sem);
    exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) {
    sem_wait(&bin_sem);
    while(strncmp("end", work_area, 3) != 0) {
        printf("You input %d characters\n", strlen(work_area) -1);
        sem_wait(&bin_sem);
    }
    pthread_exit(NULL);
}

运行程序:

$./thread3
Input some text. Enter ‘end’ to finish
The Wasp Factory
You input 16 characters
Iain Banks
You input 10 characters
end
Waiting for thread to finish...
Thread joined

实验解析:

       初始化信号量时,我们把它的值设置为 0。这样,在线程函数启动时,sem_wait 函数调用就会阻塞并等待信号量变为非零值。

       在主线程中,我们等待直到有文本输入,然后调用 sem_post 增加信号量的值,这将立刻令另外一个线程从 sem_wait 的等待中返回并开始执行。在统计完字符个数后,它再次调用 sem_wait 并再次被阻塞,直到主线程再次调用 sem_post 增加信号量的值为止。

       我们很容易忽略程序设计上的细微错误,而该错误会导致程序运行结果中的一些细微错误。我们将上面的程序稍加修改:

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

void *thread_function(void *arg);
sem_t bin_sem;

#define WORK_SIZE 1024
char work_area[WORK_SIZE];

int main() {
    int res;
    pthread_t a_thread;
    void *thread_result;

    res = sem_init(&bin_sem, 0, 0);
    if (res != 0) {
        perror("Semaphore initialization failed");
        exit(EXIT_FAILURE);
    }
    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if (res != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }

    printf("Input some text. Enter 'end' to finish\n");
    while(strncmp("end", work_area, 3) != 0) {
      if (strncmp(work_area, "FAST", 4) == 0) {
        sem_post(&bin_sem);
        strcpy(work_area, "Wheeee...");
      } else {
        fgets(work_area, WORK_SIZE, stdin);
      }
      sem_post(&bin_sem);
    }

    printf("\nWaiting for thread to finish...\n");
    res = pthread_join(a_thread, &thread_result);
    if (res != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    printf("Thread joined\n");
    sem_destroy(&bin_sem);
    exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) {
    sem_wait(&bin_sem);
    while(strncmp("end", work_area, 3) != 0) {
        printf("You input %d characters\n", strlen(work_area) -1);
        sem_wait(&bin_sem);
    }
    pthread_exit(NULL);
}

输入 FAST

$ ./thread3a
Input some text. Enter ‘end’ to finish
Excession
You input 9 characters
FAST
You input 7 characters
You input 7 characters
You input 7 characters
end
Waiting for thread to finish...
Thread joined

      问题在于,我们的程序依赖其接收文本输入的时间要足够长,这样另一个线程才有时间在主线程还未准备好给它更多的单词去统计之前统计出工作区域中字符的个数。当我们试图连续快速的给它两组不同的单词去统计时(键盘输入的 FAST 和程序自动提供的 Weee...),第二个线程就没有时间去执行。但信号量已经被增加了不止一次,所以字符统计线程就会反复统计字符数目并减少信号量的值,直到它再次变为零为止。
      这个例子显示:在多线程程序中,我们需要多时序考虑的非常仔细。为了解决上面程序中的问题,我们可以再增加一个信号量,让主线程等到统计线程完成字符个数的统计后再继续执行,但更简单的一种方式是使用互斥量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
1. 智慧监狱概述 智慧监狱的建设背景基于监狱信息化的发展历程,从最初的数字化监狱到信息化监狱,最终发展到智慧监狱。智慧监狱强调管理的精细化、监管的一体化、改造的科学化以及办公的无纸化。政策上,自2017年以来,司法部连续发布了多项指导性文件,推动智慧监狱的建设。 2. 内在需求与挑战 智慧监狱的内在需求包括数据应用与共享的不足、安防系统的单一功能、IT架构的复杂性、信息安全建设的薄弱以及IT运维的人工依赖。这些挑战要求监狱系统进行改革,以实现数据的深度利用和业务的智能化。 3. 技术架构与设计 智慧监狱的技术架构包括统一门户、信息安全、综合运维、安防集成平台和大数据平台。设计上,智慧监狱采用云计算、物联网、大数据和人工智能等技术,实现资源的动态分配、业务的快速部署和安全的主动防护。 4. 数据治理与应用 监狱数据应用现状面临数据分散和共享不足的问题。智慧监狱通过构建数据共享交换体系、数据治理工具及服务,以及基于数据仓库的数据分析模型,提升了数据的利用效率和决策支持能力。 5. 安全与运维 智慧监狱的信息安全建设涵盖了大数据应用、安全管理区、业务区等多个层面,确保了数据的安全和系统的稳定运行。同时,综合运维平台的建立,实现了IT系统的统一管理和自动化运维,提高了运维效率和系统的可靠性。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值