历史回顾---C时代的多线程设计
上一篇文章中说明了我们的线程类的设计目标是简单、实用和方便移植,然后说明了我们在Linux和Windows上的开发环境。为简单起见,在后续内容中我们将使用Linux作为主要开发平台,以g++(gcc)3.4.4为开发环境。
我们首先来回顾一下在C中是怎样设计多线程程序的。
假设我们要设计一个程序,它包含两个线程。主线程用于响应用户命令,当用户在标准输入上键入”quit”并按下回车后就结束程序,另外一个线程则每隔3秒钟在标准输出上打印一行”I am alive”消息。
这样的程序应该是下面这个样子。
// File : thread1.c
#include <stdio.h>
#include <unistd.h>
extern volatile int g_iQuitFlag;
void *thread1_function(void *arg) {
while(g_iQuitFlag != 1) {
printf("I am alive.\n");
sleep(3);
}
return NULL;
}
//--EOF--
// File : main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
volatile int g_iQuitFlag;
extern void *thread1_function(void *arg);
int main()
{
char szBuf[1024];
int iRes;
pthread_t t1;
iRes = pthread_create(&t1, NULL, thread1_function, NULL);
if (iRes != 0) {
perror("Calling pthread_create failed.");
exit(-1);
}
while(1) {
fgets(szBuf, sizeof(szBuf), stdin);
if(0 == strncmp(szBuf, "quit", 4)) {
g_iQuitFlag = 1;
break;
}
}
pthread_join(t1, NULL);
return 0;
}
//--EOF--
两个文件应该被放到同一个目录下,编译命令为:
g++ main.c thread1.c -lpthread
环境配置没有问题的话,编译器(以及被自动调用的链接器)将产生一个名为a.out的文件。执行该文件:
xxxx$ ./a.out
到目前为止,一切看起来都很顺利。我们为子线程编写一个例程函数,然后在主线程创建子线程。主线程中通过改变全局变量的值来通知子线程结束自己。主线程在设置该全局变量后等待子线程结束后退出,程序结束。
上面的例子中有一个小问题,就是用户在输入“quit”后程序不是立即退出的,需要等一小会。这是因为子线程每隔3秒钟检查一次退出标志,在下一个3秒钟到来之前子线程是没有响应的。我们在稍后的文章中将介绍一个更好的办法来解决这个问题。
这个程序的Windows版本如下。
// File : thread1.c
#include <Windows.h>
#include <stdio.h>
extern volatile int g_iQuitFlag;
DWORD WINAPI thread1_function(LPVOID arg) {
while(g_iQuitFlag != 1) {
printf("I am alive.\n");
Sleep(3 * 1000);
}
return 0;
}
//--EOF--
// File : main.c
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
volatile int g_iQuitFlag;
extern DWORD WINAPI thread1_function(LPVOID arg);
int main()
{
char szBuf[1024];
HANDLE t1;
t1 = CreateThread(NULL,
0,
thread1_function,
NULL,
0,
NULL);
if (t1 == NULL) {
perror("Calling pthread_create failed.");
exit(-1);
}
while(1) {
fgets(szBuf, sizeof(szBuf), stdin);
if(0 == strncmp(szBuf, "quit", 4)) {
g_iQuitFlag = 1;
break;
}
}
WaitForSingleObject(t1, INFINITE);
CloseHandle(t1);
return 0;
}
//--EOF--
注意到Windows的Sleep单位是毫秒,而Linux的sleep单位是秒。另外Windows的线程句柄需要在线程结束后被关闭。
我们可以使用IDE建立一个程序项目来编译程序,也可以使用命令行来编译。命令行的编译指令为:
cl main.c thread1.c /MT
注意VC++的多线程库开关MT。使用IDE的场合也需要打开多线程库支持开关。