29

异步编程举例之多线程版本闹钟

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzUxMTk4MzY3MA%3D%3D&%3Bmid=2247483789&%3Bidx=1&%3Bsn=764c69a3c713aa9e7f2f153155adebb2&%3Butm_source=tuicool&%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

现在让我们看一个和多进程版本相似的闹钟程序,但它是用多线程实现的。该例子中用到的三个 Pthreads 函数:

pthread_create : 创建一个线程,运行由第三个参数 (alarm_thread) 指定的例程 ( 具体见下面例子 ) ,并返回线程标识符 ID( 保存在 thread 引用的变量中 )

  pthread_detach : 当线程终止时立刻回收线程资源

  pthread_exit: 终止线程调用

数据结构 alarm_t 中定义了每个闹钟的命令信息, seconds 中存储等待时间, message 中存储显示文本。

#include <pthread.h>

#include "errors.h"

typedef struct alarm_tag {

int         seconds;

char        message[64];

} alarm_t;

函数 alarm_thread 是闹钟线程,即创建的每个闹钟线程执行的函数,该函数返回时,闹钟线程终止。该函数参数 (void *arg) 是传给 pthread_create 函数的第四个参数,即 alarm_t 结构体指针。线程首先将 void* 参数转为 alarm_t* 类型,然后调用 pthread_detach 函数来分离自己,作用是通知 Pthreads 不必关心它的终止时间与退出状态。

线程睡眠指定的时间 ( alarm_t 中的 seconds 决定 ) ,之后打印指定的消息文本,最后释放 alarm_t 结构体空间并返回,线程终止。通常, Pthreads 会保存线程的资源以供其他线程了解它已经终止并获得其最终结果。由于本例中线程负责分离自己,所以不必做上述工作。

void *alarm_thread (void *arg)

{

alarm_t *alarm = (alarm_t*)arg;

int status;

status = pthread_detach (pthread_self ());

if (status != 0)

err_abort (status, "Detach thread");

sleep (alarm->seconds);

printf ("(%d) %s\n", alarm->seconds, alarm->message);

free (alarm);

return NULL;

}

线程版本闹钟的 main() 函数与之前的两个版本相同,循环读取命令行、解析命令行直到不能从 stdin 中读取数据为止。

创建一个闹钟线程,它以 alarm_t 为线程参数运行函数 alarm_thread

int main()

{

int status;

char line[128];

alarm_t *alarm;

pthread_t thread;

while (1) {

printf ("Alarm> ");

if (fgets (line, sizeof (line), stdin) == NULL) exit (0);

if (strlen (line) <= 1) continue;

alarm = (alarm_t*)malloc (sizeof (alarm_t));

if (alarm == NULL)

errno_abort ("Allocate alarm");

if (sscanf (line, "%d %64[^\n]",

&alarm->seconds, alarm->message) < 2) {

fprintf (stderr, "Bad command\n");

free (alarm);

} else {

status = pthread_create (

&thread, NULL, alarm_thread, alarm);

if (status != 0)

err_abort (status, "Create alarm thread");

}

}

}

总结 :比较两个异步版本闹钟程序是理解线程编程很好的选择。在 fork 版本中,每个闹钟有一个从主进程拷贝的独立地址空间,这意味着可以将闹钟时间和显示文本放在局部变量中,一旦创建了子进程,父进程就可以改变这些变量而不会影响闹钟子进程。在多线程版本中,所有线程共享同一个地址空间,所以可为每个闹钟调用 malloc 建立新的 alarm_t 结构体,并传递给新建线程。

在使用 fork() 版本中,主进程要调用 waitpid 函数来通知系统释放其创建的子进程资源。在多线程版本中,不需要等待线程结束,除非希望获得它的返回值;每个线程分离自己,故该线程的资源在它终止后会立刻回收。

在实际应用中,不会为每个闹钟建立一个进程。你可能轻易设置上百个闹钟活动,但是系统可能无法创建那么多进程。但是对应可以在一个进程中创建几百个线程。

另外可以将常用的头文件以及一些宏定义包含在一个头文件中,比如 #include "errors.h" 。本次程序的运行环境依然是 Qt 的控制台程序。

彩蛋 :一个更加成熟的闹钟版本可以只有两个线程:一个负责读取用户输入,一个等待闹钟停止。之后的学习会逐步实现该版本。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK