- 本身是非并发的问题,如果使用了多线程只会降低程序的性能并使用程序复杂。
例如:
如果程序中的每一步都需要上一步的结果,则使用多线程不会有任何帮助,因为每个线程都不得不等待其它线程的结束。
最适合使用线程的应用:
1. 计算密集型应用
2. I/O密集型应用
基本的线程同步模型为:使用互斥量保护共享数据,使用条件变量来通信,还可以使用其它的同步机制,如信号量、管道、消息队列。
- | 开始运行的主线程-初始线程(main)是特殊的,它的特殊性在于main函数返回时会导致所在进程终止。即,进程结束时不会等待其它线程的结束,当进程结束时,所有线程、状态和它们的工作结果都会被无理由的全部清理掉。所以要确保main函数不会早于线程结束。
pthread_equal
函数返回非0值,否则返回0值。pthread_self
函数用来获取自身线程IDpthread_eixt
函数用来终止自身线程wait
或detach
这个线程pthread_create
pthread_detach
函数来分离它pthread_join
,这个函数会阻塞调用者,直到指定的线程结束,并得到其返回值。errors.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
main.cpp
#include
#include "errors.h"
// 线程函数
void * thread_routine(void* arg)
{
int value = *(int*)arg;
delete (int*)arg;
printf("thread_routine is called: %d\n", value);
return arg;
}
int main()
{
pthread_t thread_id;
void *thread_result = (void*)100;
int status;
// 第4个参数用以传递给线程函数
int* arg = new int;
*arg = 123;
// 创建一个线程
status = pthread_create(&thread_id, NULL, thread_routine, (void*)arg);
if (status != 0)
err_abort(status, "Create thread");
// join会阻塞主线程,直到子线程运行结束
// 当join返回时,被join的线程就已经被detach,再也不能join了
// 如果不关心返回值,则第二个参数可以置NULL
status = pthread_join(thread_id, &thread_result);
if (status != 0)
err_abort(status, "Join thread");
if (thread_result == NULL)
printf("thread_result is NULL\n");
else
printf("thread_result is not NULL\n");
return 0;
}
结果输出
thread_routine is called: 123
thread_result is not NULL
状态 | 含义 |
---|---|
就绪(ready) | 线程能够运行,但在等待可用的处理器,可能刚刚启动,或刚刚从阻塞中恢复,或者被其它线程抢占 |
运行(running) | 线程正在运行。在多处理器系统中,可以有多个线程处于运行状态 |
阻塞(blocked) | 线程由于等待处理器外的其他条件无法运行,如条件变量的改变、加锁互斥量或I/O操作结束 |
终止(terminated) | 线程从起始函数中返回,或调用pthread_exit ,或者被取消,终止自己并完成所有资源清理工作。一旦线程被分离或连接,它就可以被收回 |
pthread_create
SIGEV_THREAD
,则当进程收到一个POSIX信号时也会创建线程新的线程被创建后,是处于就绪状态
注意:
受调试机制的限制,新的线程可能在就绪状态停留一段时间后才开始执行。但当前线程从函数pthread_create
中返回及新线程被调试执行之间不存在同步关系。即,新线程可能在当前线程从pthread_create返回之前就运行了,甚至在其返回之前,新线程就已经运行完毕了。
线程被创建时,指定的线程函数(及其参数)就执行了
大多数线程会不时的睡眠,线程之所以会睡眠是因为它需要的某个资源不可用(即被阻塞),或者因为系统将处理器分配给其它线程(即抢占)。
就绪状态
,就绪
意味着线程在等待可用的处理器。就绪状态
。就绪
线程执行它时,该线程进入运行状态
。通常这意味着可能某个其它的线程被阻塞或者被时间片机制抢占,则处理器会保存被阻塞(或被抢占)线程的环境并恢复一个就绪
线程的环境。就绪
线程而不必阻塞其它正在运行
的线程。阻塞状态
重新进入就绪状态
。如果处理器可用,它可以马上进入到运行状态
。pthread_eixt
退出线程 或 调用 pthread_cancel
取消线程,它们会触发清理函数后再进入终止状态。 pthread_exit
时设定。 PTHREAD_CANCELLED
僵线程
如果一个线程已经被分离,则它会立即进入回收,否则,线程处于终止状态,它还可以被其它线程调用pthread_join 连接。这种线程被称为僵线程
,因为它虽然已经死了但还存在。僵线程可能会保留其运行时的大部分甚至所有资源。因此,不应该让线程长时间处于这种状态,当创建不需要连接的线程时,应该使用 detachstate 属性建立线程使其自动分离。
pthread_join是有用的,但不是必要的
当你需要从线程中获取返回值时,一般情况下肯定是要建立分离的线程,这与 pthread_join 相 矛盾。
可以不使用join,方法如下:
传递一个某种形式的数据结构给某个工作线程,且使该数据结构能够被其它线程访问,你可以在工作线程中改变数据结构后,广播某个条件变量。做完这些,这个工作线程就可以被立即回收了,而你关心的返回值或数据始终保存在你随时可以找到的地方。
注意点:当你所关心的线程终止了,则它里面所有资源就不该再被访问了