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

 

现在让我们看一个和多进程版本相似的闹钟程序,但它是用多线程实现的。该例子中用到的三个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的控制台程序。

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

 

欢迎大家共同交流:

发现在微信上不适合看程序,读文字还好~~~

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值