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

POSIX线程-(六)

阅读更多

线程属性-调度

下面我们来看一下我们也许希望改变的第二个线程属性:调度。改变调度属性与设置分离属性相类似,但是我们可以使用另外两个函数来查找可用的等级级别,sched_get_priority_max与sched_get_priority_min。

试验--调度


因为thread6.c是与前面的例子十分类似,这里我们只看一下其中的区别。

1 首先,我们需要一些额外的变量:

int max_priority;
int min_priority;
struct sched_param scheduling_value;

2 在我们设置分离属性之后,我们设置调度策略。

res = pthread_attr_setschedpolicy(&thread_attr, SCHED_OTHER);
if (res != 0) {
perror("Setting scheduling policy failed");
exit(EXIT_FAILURE);
}

3 接下来我们查看允许级别的范围:

max_priority = sched_get_priority_max(SCHED_OTHER);
min_priority = sched_get_priority_min(SCHED_OTHER);

4 并且设置一个级别:

scheduling_value.sched_priority = min_priority;
res = pthread_attr_setschedparam(&thread_attr, &scheduling_value);
if (res != 0) {
perror("Setting scheduling priority failed");
exit(EXIT_FAILURE);
}

工作原理

这个例子与设置分离状态属性相类似,所不同的是我们设置调度策略。

关闭线程

有时我们希望一个线程可以请求另一个线程结束,而不是向其发送一个信号。线程有一个方法可以完成这个任务,而且并行使用信号处理,线程具有一个在他们被请求结束时修改其行为的方法。

首先我们来看一下请求线程结束的函数:

#include <pthread.h>
int pthread_cancel(pthread_t thread);

这个函数声明很显示:给定一个线程标识符,我们可以请求其关闭。在收到关闭请求之后,事情有一些复杂,但是并不是很多。一个线程可以使用pthread_setcancelstate来设置其关闭状态。

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);

第一个参数或者为PTHREAD_CANCEL_ENABLE,这个参数允许线程接收关闭请求,或者是PTHREAD_CANCEL_DISABLE,这个参数会使得函数忽略关闭请求。oldstate指针允许获取前一个状态。如果我们对这个状态不感兴趣,我们可以简单的传递一个NULL。如果接受了关闭请求,线程可以采用一个第二级控制,关闭类型,这是由pthread_setcanceltype来设置的。

#include <pthread.h>
int pthread_setcanceltype(int type, int *oldtype);

这个类型的值可以为PTHREAD_CANCEL_ASYNCHRONOUS,这个类型可以在接收到关闭请求后立即执行,或者为PTHREAD_CANCEL_DEFERRED,这个类型会使得关闭请求等待直到执行下列函数中的一个:pthread_join,pthread_cond_wait,pthread_cond_timedwait,pthread_testcancel,sem_wait或者是sigwait。

在这一章我们并没有看到这些函数,因为并不是所有的函数都需要。如以前的一样,更为详细的信息可以在手册中查看。

再一次说明,oldtype允许获取前一个状态,如果我们对前一个状态不感兴趣,我们可以传递一个NULL。默认情况下,线程以关闭状态PTHREAD_CANCEL_ENABLE与关闭状态PTHREAD_CANCEL_DEFERRED开始。

试验--关闭线程

我们这里的程序thread7.c是由thread1.c发展来的。这一次,主线程向其所创建的线程发送一个关闭请求。

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

void *thread_function(void *arg);

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

res = pthread_create(&a_thread,NULL,thread_function,NULL);
if(res != 0)
{
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(3);
printf("Canceling thread...\n");
res = pthread_cancel(a_thread);
if(res != 0)
{
perror("Thread cancelation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish...\n");
res = pthread_join(a_thread,&thread_result);
if(res != 0)
{
perror("Thread join failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}

void *thread_function(void *arg)
{
int i, res;
res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
if(res != 0)
{
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
if(res != 0)
{
perror("Thread pthread_setcanceltype failed");
exit(EXIT_FAILURE);
}
printf("Thread_function is running\n");
for(i=0;i<10;i++)
{
printf("Thread is still running (%d)...\n",i);
sleep(1);
}
pthread_exit(0);
}

工作原理

在用通常的方法创建一个新线程之后,主线程休眠(允许新线程启动)并且发送一个关闭请求。

sleep(3);
printf("Canceling thread...\n");
res = pthread_cancel(a_thread);
if(res != 0)
{
perror("Thread cancelation failed");
exit(EXIT_FAILURE);
}

在所创建的线程中,我们首先设置关闭状态允许关闭。

res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
if(res != 0)
{
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}

然后我们设置关闭类型。

res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
if(res != 0)
{
perror("Thread pthread_setcanceltype failed");
exit(EXIT_FAILURE);
}

最后线程等待关闭。

for(i=0;i<10;i++)
{
printf("Thread is still running (%d)...\n",i);
sleep(1);
}

多线程

直到现在,我们总是使得程序的一个执行线程只创建一个线程。然而,我们不希望使得大家认为我们只能创建一个线程。

试验--多线程

作为我们本章的最后一个例子,thread8.c,我们演示如何在同一个程序中创建多个线程,并且以他们启动的不同顺序来收集这些线程。

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

#define NUM_THREADS 6

void *thread_function(void *arg);

int main()
{
int res;
pthread_t a_thread[NUM_THREADS];
void *thread_result;
int lots_of_threads;

for(lots_of_threads = 0;lots_of_threads < NUM_THREADS; lots_of_threads++)
{
res = pthread_create(&(a_thread[lots_of_threads]),NULL,thread_function,(void *)&lots_of_threads);
if(res != 0)
{
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}
printf("Waiting for threads to finish...\n");
for(lots_of_threads = NUM_THREADS -1;lots_of_threads >=0;lots_of_threads--)
{
res = pthread_join(a_thread[lots_of_threads],&thread_result);
if(res == 0)
{
printf("Picked up a thread\n");
}
else
{
perror("pthread_join failed");
}
}
printf("All done\n");
exit(EXIT_SUCCESS);
}

void *thread_function(void *arg)
{
int my_number = *(int *)arg;
int rand_num;

printf("thread_function is running. Argument was %d\n",my_number);
rand_num = 1+(int)(9.0*rand()/(RAND_MAX+1.0));
sleep(rand_num);
printf("Bye from %d\n",my_number);
pthread_exit(NULL);
}

当我们运行这个程序时,我们会得到下面的输出:

$ ./thread8
thread_function is running. Argument
thread_function is running. Argument
thread_function is running. Argument
thread_function is running. Argument
thread_function is running. Argument
Bye from 1
thread_function is running. Argument
Waiting for threads to finish...
Bye from 5
Picked up a thread
Bye from 0
Bye from 2
Bye from 3
Bye from 4
Picked up a thread
Picked up a thread
Picked up a thread
Picked up a thread
Picked up a thread
All done

正如我们所看到的,我们创建了多个线程,并且允许他们以乱序结束 。在这个程序中存在一个bug,如果我们由启动线程的循环中移除sleep调用就会得到验证。我们包含就要演示当我们使用线程编写程序时需要小心。可以定位吗?我们会在下面的"工作原理"部分进行解释。

工作原理

这一次我们创建一个线程ID数组:

pthread_t a_thread[NUM_THREADS];

并且循环来创建多个线程:

for(lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
res = pthread_create(&(a_thread[lots_of_threads]), NULL,
thread_function, (void *)&lots_of_threads);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}

这些线程在退出之前等待一个随机时间:

void *thread_function(void *arg) {
int my_number = *(int *)arg;
int rand_num;
printf("thread_function is running. Argument was %d\n", my_number);
rand_num=1+(int)(9.0*rand()/(RAND_MAX+1.0));
sleep(rand_num);
printf("Bye from %d\n", my_number);
pthread_exit(NULL);
}

而在主线程中,我们等待收集这些线程,但是并不是以他们被创建的顺序。

for(lots_of_threads = NUM_THREADS - 1; lots_of_threads >= 0; lots_of_threads—)
{
res = pthread_join(a_thread[lots_of_threads], &thread_result);
if (res == 0) {
printf("Picked up a thread\n");
}
else {
perror("pthread_join failed");
}
}

如果我们运行没有sleep调用的程序,我们就会看到一些奇怪的作用,包括一些线程使用相同的参数启动。我们是否定位这些情况了呢?线程正在使用一个局部变量作为线程函数的参数进行启动。这个变量是在循环中进行更新的。这个更新发生在下面的代码行中:

for(lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
res = pthread_create(&(a_thread[lots_of_threads]), NULL,
thread_function, (void *)&lots_of_threads);

如果主线程运行得足够快,他就会为某些线程修改这个参数。类似的行为在使用共享变量与多执行路径时没有引起足够的注意造成的。我们已经警告过大家线程需要小心的设计。要修改这个问题,我们需要像如下的样子来直接传递这个值:

res = pthread_create(&(a_thread[lots_of_threads]), NULL, thread_function, (void
*)lots_of_threads);

并且修改thread_function:

void *thread_function(void *arg) {
int my_number = (int)arg;

小结

在这一章,我们了解了如何在一个进程内部创建多个执行线程,而其中每个线程共享文件域变量。然后我们探讨了线程控制对临界代码与数据的访问方法,使用信号量与互斥。在此之后,我们讨论了线程属性的控制,而且讨论了我们如何将他们与主线程分离,从而他不再等待他所创建的线程结束。在快速了解了线程如何请求另一个线程完成与接收线程如何管理这些请求之后,我们提供了一个多个并行线程执行的例子。

我们并没有足够的空间来讨论每一个函数调用与相关的线程,但是我们现在应具有足够的知识来使用线程编写我们的程序,并且可以通过阅读手册页来探讨更多的线程知识。

分享到:
评论

相关推荐

    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(结构化异常处理)机制...

    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_...

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

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

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

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

    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等。 该线程池封装的主要特点是防止任务丢失。在高并发环境下,如果线程池的大小固定...

    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指的是实时...

    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

    ### Posix线程编程指南:线程创建与取消详解 #### 一、线程与进程的概念及区别 在深入探讨Posix线程编程之前,理解线程与进程的基本概念及其区别至关重要。线程,作为执行体的一个更轻量级单位,能够在同一进程中...

    Posix线程编程指南

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

Global site tag (gtag.js) - Google Analytics