线程是指机器中连续的、顺序的属性集合。一个线程包含执行一系列机器指令所必须的机器状态,包括当前指令位置、地址和数据寄存器等。
一个UNIX进程可以理解为一个线程加上地址空间、文件描述符和其他数据。
1 术语
异步:
任何两个彼此独立运行的操作是异步的。异步表明事情相互独立地发生,除非强加的依赖性。
异步带来的最大复杂性就是:如果你没有同时执行多个活动,那么异步就没有什么优势。如果你开始了一个异步活动,然后什么也不做等待它结束,则你并没有从异步那里获得太多好处。
并发:
指让实际上可能串行发生的事情好像同时发生一样。
并行:
指并发序列同时执行。
真正的并行只能在多处理器系统中存在,但是并发可以在单处理器系统和多处理器系统中都存在。并发能够在但处理器系统中存在是因为并发实际上是并行的假象。并行要求程序能够同时执行多个操作,而并发只要求程序能够假装同时执行多个操作。
线程安全:
指代码能够被多个线程调用而不会产生灾难性结果。它不要求代码在多个线程中高效地运行,只要求能够安全地运行。大部分现行函数可以利用互斥量、条件变量和线程私有数据来实现线程的安全。
函数可重入:
可重入有时用来表示“有效的线程安全”,即通过采用比将函数或库转换成一系列区域更为复杂的方式使代码成为线程安全的。通过引入互斥量和线程私有数据可以实现线程安全,但通常需要改变接口来使函数可重入。
可重入的函数应该避免依赖任何静态数据,最好避免依赖线程间任何形式的同步。
2 线程的好处
1)在多处理器系统中开发程序的并行性。
2)在等待慢速外设I/O操作结束的同时,程序可以执行其他计算,为程序的并发提供更有效、更自然的开发方式。
3)一种模块化编程模型,能清晰地表达程序中独立事件间的相互关系。
3 线程的代价
1)计算负荷。线程代码中的同步机制容易损失性能。
2)编程规则。了解同步协议,避免死锁、竞争和优先级倒置。
3)难以调试。
4 线程的最佳应用
1)计算密集型应用,为了能在多处理器系统上运行,将这些计算分解到多个线程中实现。
2)I/O密集型应用,为了提高性能,将I/O操作重叠。很多线程可以同时等待不同的I/O操作。分布式服务器应用就是很好的实例,它们必须响应多个客户的请求,必须为通过慢速的网络连接主动提供I/O做好准备。
5 线程报错方式
在传统的UNIX系统中,errno是一个外部整型变量。由于该变量一次只有有一个值,所以只能支持进程中的单一执行流程。errno以前使用的定义是:
extern int errno;
1)线程内errno变量
在支持线程的环境中,多个线程共享进程地址空间,每个线程都有属于它自己的局部errno以避免一个线程干扰另一个线程。Linux支持多线程存取errno,将其定义为:
extern int*__errno_location(void);
#define errno(*__errno_location())
2)线程相关函数通过返回值来表示错误状态。如,pthread_create函数,正确返回0,出错返回错误码。
闹钟示例:
/* errors.h*/
#ifndef __errors_h
#define __errors_h
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef DEBUG
#define DPRINTF(arg) printf arg
#else
#define DPRINTF(arg)
#endif
#define err_abort(code,text) do{\
fprintf(stderr, "%s at \"%s\":%d: %s\n",\
text, __FILE__, __LINE__, strerror(code));\
abort();\
}while(0)
#define errno_abort(text) do {\
fprintf(stderr, "%s at \"%s\":%d: %s\n", \
text, __FILE__, __LINE__, strerror (errno)); \
abort();\
}while(0)
#endif
/*alarm_main.c V1*/
#include "errors.h"
int main(int argc, char *argv[])
{
int seconds;
char line[128];
char message[64];
while (1)
{
printf("Alarm> ");
if (fgets(line, sizeof(line), stdin) == NULL)
exit(0);
if (strlen(line) <= 1)
continue;
if (sscanf(line, "%d %64[^\n]", &seconds, message) < 2)
{
fprintf(stderr, "Bad command\n");
}
else
{
sleep(seconds);
printf("(%d) %s\n", seconds, message);
}
}
}
/*alarm_fork.c V2*/
#include "errors.h"
#include <wait.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
pid_t pid;
int status;
int seconds;
char line[128];
char message[64];
while (1)
{
printf("Alarm> ");
if (fgets(line, sizeof(line), stdin) == NULL)
exit(0);
if (strlen(line) <= 1)
continue;
if (sscanf(line, "%d %64[^\n]", &seconds, message) < 2)
{
fprintf(stderr, "Bad command\n");
}
else
{
pid = fork();
if (pid == (pid_t)-1)
{
errno_abort("fork");
}
if (pid == (pid_t)0)
{
sleep(seconds);
printf("(%d) %s\n", seconds, message);
exit(0);
}
else
{
do{
pid = waitpid((pid_t)-1, NULL, WNOHANG);
if (pid == (pid_t)-1)
errno_abort("Wait for child");
}while (pid != (pid_t)0);
}
}
}
}
/*alarm_thread.c V3*/
#include "errors.h"
#include <pthread.h>
typedef struct alarm_tag
{
int seconds;
char message[64];
}alarm_t;
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;
}
int main(int argc, char *argv[])
{
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");
}
}
}
参考:
《POSIX多线程程序设计》