`
mylxiaoyi
  • 浏览: 328429 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

POSIX线程-(四)

阅读更多

使用互斥同步

在多线程程序中同步访问的另一个方法就是使用互斥,其作用允许程序锁住一个对象,从而只有一个线程可以访问他。要控制对临界区代码的访问,在我们进入这段代码之前锁住一个互斥量,并且在我们完成操作时进行解锁。

使用互斥所需要基本函数与信号量所需要的函数相似,其声明如下:

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t
*mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

如平常一样,成功时返回0,如果失败则会返回一个错误代码,但是并没有设置errno;我们必须使用返回代码。

与信号量相类似,这些函数以一个指向前面声明的对象的指针为参数,在互斥方法是一个pthread_mutex_t。额外的属性参数pthread_mutexattr_t允许我们为互斥提供属性,这可以控制其行为。属性类型默认为"fast"。这有一个小的缺点,如果我们的程序试着在一个已经上锁的互斥量上调用pthread_mutex_lock,程序就会阻塞。因为拥有锁的线程现在被阻塞了,互斥量就不会被解锁,从而程序就会进入死锁状态。可以修改互斥量的属性,从而他或者可以检测这种情况并返回一个错误,或者是循环操作并且在同一个线程上允许多个锁。

设置互斥量的属性超出了本书的范围,所以我们会为属性指针传递NULL并且使用默认行为。我们可以通过阅读pthread_mutex_init手册页了解修改属性的内容。

试验--线程互斥

再一次说明,下面的程序是我们原始程序thread1.c的修改版,但是进行了大量的修改。这一次,我们有一些偏狂来访问我们的临界变量,并且使用一个互斥量来保证每次只有一个线程访问他们。为了使得代码更易于阅读,我们忽略了由互斥量加锁与解锁操作返回值的错误检测。在生产代码中,我们应该检测这些返回值。下面是这个新程序,thread4.c:

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

void *thread_function(void *arg);
pthread_mutex_t work_mutex;

#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;

int main()
{
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex,NULL);
if(res != 0)
{
perror("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread,NULL,thread_function,NULL);
if(res != 0)
{
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf("Input some text, Enter 'end' to finish\n");
while(!time_to_exit)
{
fgets(work_area,WORK_SIZE,stdin);
pthread_mutex_unlock(&work_mutex);
while(1)
{
pthread_mutex_lock(&work_mutex);
if(work_area[0] != '\0')
{
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else
{
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
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");
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}

void *thread_function(void *arg)
{
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp("end",work_area,3) != 0)
{
printf("You input %d characters\n",strlen(work_area)-1);
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while(work_area[0] == '\0')
{
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}

$ cc -D_REENTRANT -I/usr/include/nptl thread4.c –o thread4 -L/usr/lib/nptl -
lpthread
$ ./thread4
Input some text. Enter ‘end’ to finish
Whit
You input 4 characters
The Crow Road
You input 13 characters
end
Waiting for thread to finish...
Thread joined

工作原理

我们开始声明了一个互斥量,我们的工作区,而且这次,我们声明了一个额外变量:time_to_exit。

pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;

然后我们初始化互斥量

res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0) {
perror(“Mutex initialization failed”);
exit(EXIT_FAILURE);
}

接下来我们开始我们的新线程。下面是线程函数内部执行的代码:

pthread_mutex_lock(&work_mutex);
while(strncmp(“end”, work_area, 3) != 0) {
printf(“You input %d characters\n”, strlen(work_area) -1);
work_area[0] = ‘\0’;
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == ‘\0’ ) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = ‘\0’;
pthread_mutex_unlock(&work_mutex);

首先,新线程尝试锁住这个互斥量。如果他已经被锁住了,调用就会阻塞直到互斥量被释放。一旦我们访问,我们进行检测来查看我们是否正被请求退出。如果我们被请求退出,我们只是简单的设置time_to_exit,清除工作区的第一个字符,并且退出。

如果我们不希望退出,我们计算字符数然后清空第一个字符为空。我们使用将第一个字符清空的方法来通知读取程序我们已经完成了计算。我们然后解锁互斥量并且等待主线程运行。我们会循环尝试锁住这个互斥量,当我们成功时,我们会检测主线程是否为我们指定了更多的工作要做。如果没有,我们解锁互斥量并等待一段更长的时间。如果有,我们计算字符数并且再次进入循环。

下面是主线程:

pthread_mutex_lock(&work_mutex);
printf(“Input some text. Enter ‘end’ to finish\n”);
while(!time_to_exit) {
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1) {
pthread_mutex_lock(&work_mutex);
if (work_area[0] != ‘\0’) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else {
break;
}
}
}
pthread_mutex_unlock(&work_mutex);

这与上面所说的线程类似。我们锁住工作区域,从而我们可以向其中读取文本,然后解锁允许其他的线程访问来计算单词数。循环往复,我们重新锁住互斥量,检测单词数是否进行了计算,如果我们需要等待更长的时间,我们就会释放这个锁。正如我们在前面所注意到的,这并不是一个好的编程习惯,而在真实的世界中,我们可以使用信号量来避免这种情况。然而,这里的代码只是作为一个例子。

分享到:
评论

相关推荐

    x86-64-12.2.0-release-posix-seh-msvcrt-rt-v10-rev2

    标题 "x86-64-12.2.0-release-posix-seh-msvcrt-rt-v10-rev2" 指的是一个针对x86_64架构的GCC编译器版本,该版本是12.2.0,定位为“release”发布版,具有POSIX线程(pthread)支持,使用SEH(结构化异常处理)机制...

    x86-64-posix-seh和MinGW-W64-install.exe

    SEH在处理异常时具有高效性和线程安全的特性,特别适合多线程环境。而在64位模式下,x86-64-posix-seh是对SEH的一种扩展,它遵循POSIX标准,使得在Windows系统上编译的程序可以更好地兼容Unix-like系统的行为。 ...

    i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z

    MinGW-W64的32位安装包:i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z ,支持GCC8.1.0,线程模型为posix,异常处理模型为dwarf, 解压后即可使用。 mingw-w64 版本选择方法: 32位系统选择i686, 64位系统选择x86_...

    mingw-x86-64-8.1.0-release-posix-seh-rt-v6-rev

    在某些情况下,如在多线程程序或者需要跨平台兼容性的项目中,开发者可能会选择SJLJ。 这个版本的MinGW-w64包含了GCC(GNU Compiler Collection),这是一个开源的编译器套件,支持多种编程语言,包括C、C++、...

    x86_64-8.1.0-release-posix-seh-rt_v6-rev0 (1).zip

    标题 "x86_64-8.1.0-release-posix-seh-rt_v6-rev0 (1).zip" 提供的信息表明,这是一个针对x86_64架构的软件包,版本号为8.1.0,类型是release(正式发布版),并且具有POSIX线程(posix-seh)支持,RT_v6指的是实时...

    i686-7.3.0-release-posix-dwarf-rt_v5-rev0.7z

    线程模型:win32 : 没有C ++ 11多线程特性, posix : 支持C ++ 11多线程特性; 异常处理模型:32位系统推荐dwarf,64位系统推荐seh。seh 是新发明的,而 sjlj 则是古老的。seh 性能比较好,但不支持 32位, sjlj 稳定...

    windows posix 线程库

    本文将详细讨论“Windows POSIX线程库”及其在Windows环境下实现POSIX线程(pthreads)的功能,以及如何利用该库实现Linux多线程代码在Windows上的运行。 POSIX线程库,通常称为pthreads,是UNIX系统中广泛使用的多...

    (中英文)-POSIX多线程程序设计-Programming with POSIX Threads

    本书深入浅出地介绍了POSIX线程(也称为pthreads)API,是理解并掌握多线程编程的重要参考资料。在本文中,我们将探讨POSIX线程的基本概念、API用法以及在实际开发中的应用。 1. **POSIX线程基础** - POSIX线程,...

    POSIX线程编程指南

    ### POSIX线程编程指南 #### 一、线程创建 ##### 1.1 线程与进程 在计算机科学领域,线程是程序执行的基本单位之一,它比进程更接近于执行体的概念。线程可以与其他同属于一个进程中的线程共享数据,但是每个线程...

    PosixThread - 20200228.rar

    利用Posix线程,开发者可以编写出高度可移植的多线程程序,使得代码可以在各种支持Posix的平台上运行,如Linux、macOS、FreeBSD等。 该线程池封装的主要特点是防止任务丢失。在高并发环境下,如果线程池的大小固定...

    posix线程详解.pdf(中文版)

    ### Posix线程详解 #### 1. 线程与进程的概念对比 在计算机科学领域,线程和进程都是实现程序并发执行的基本单位,但它们之间存在显著的区别。线程,尤其是POSIX线程(Portable Operating System Interface ...

    mingw-w64_x86_64-7.1.0-release-posix-seh-rt_v5-rev0

    7. **rt_v5**: 这可能指的是Runtime Library的版本,rt表示运行时库,用于提供基本的系统功能,如内存分配、线程管理和动态链接等。"v5"表示第五版,意味着这个版本可能包含了之前的版本改进和新增功能。 8. **rev0...

    Posix线程(经典)

    Posix线程(经典) Posix线程是指符合 POSIX 标准的多线程技术,可以将一个进程分解成多条执行线程,每条线程都可以独立运行,并共享所在进程的资源。Posix线程技术可以提高系统的并发性和响应速度,提高系统的性能和...

    x64-4.8.1-release-posix-seh-rev5

    这个版本是4.8.1,并且采用了“release”构建类型,POSIX线程模型("posix-seh")以及异常处理机制("seh")。"rev5"则表示这是一个修订版,可能是对先前版本的改进或修复。 描述中提到的"ming64"是MinGW-w64的一...

    POSIX多线程程序设计.pdf

    《POSIX多线程程序设计》深入描述了IEEE的开放系统接口标准——POSIX线程,通常称为Pthreads标准。本书首先解释了线程的基本概念,包括异步编程、线程的生命周期和同步机制;然后讨论了一些高级话题,包括属性对象、...

    POSIX多线程程序设计

    内容提要, 本书深入描述了IEEE的开放系统接口标准-POSIX线程,通常称为Pthreads标准。本, 书首先解释了线程的基本概念,包括异步编程、线程的生命周期和同步机制;然后讨论了, 一些高级话题,包括属性对象、线程私有...

    POSIX线程编程指南(合集)

    POSIX线程编程指南,通常被称为Pthreads,是操作系统接口的一部分,用于支持多线程编程。这个合集可能包含了深入的教程和实例,帮助开发者理解和掌握如何在遵循POSIX标准的系统上创建和管理线程。以下是关于POSIX...

    Posix线程编程指南.pdf

    #### 四、Linux下的Posix线程实现 在Linux环境下,Posix线程的实现依赖于内核提供的进程创建接口`do_fork()`。实际上,Linux线程(也称为LWP,Lightweight Process)的创建是在内核之外完成的,通过用户空间库实现...

    Posix线程编程指南

    POSIX线程编程是指使用POSIX线程(通常称为pthread)库在类Unix操作系统上进行多线程编程的过程。POSIX线程库是一系列用于创建和管理线程的C语言接口,它被广泛应用于Linux和其他类Unix系统中。该指南将详细介绍...

Global site tag (gtag.js) - Google Analytics