Introduction
首先介绍线程的概念,在网上有这么一段介绍
Thread is an execution unit which consists of its own program counter, a stack, and a set of registers. Threads are also known as Lightweight processes. Threads are popular way to improve application through parallelism. The CPU switches rapidly back and forth among the threads giving illusion that the threads are running in parallel.
As each thread has its own independent resource for process execution, multpile processes can be executed parallely by increasing number of threads.
结合其他的介绍,总结来说:
- 线程不是进程 但可以理解为轻量级进程 通常来说是进程的表亲
- 多个线程可以共享产生它们的父进程的全部系统资源,包括虚拟地址空间、文件描述符、信号处理等等。
- 线程有自己的私有资源,例如计数器、栈、寄存器等。
多线程的优点
简单来说,在进程间切换相对于在线程间切换,开销要大很多,尤其是如果需要多个进程同时运行的话,频繁切换会造成很大的系统开销,可能我们的本意是通过多进程提高程序的运行速度,但是往往会发现,多进程之间的通信开销、系统切换的开销有时候会造成程序的执行速度反而降低了,这个时候就用到了多线程,由于多个线程之间可以共享父进程的资源,可以省去相当一部分进程间的通信开销,同时线程间的切换比进程间的切换要快很多,所以在一些程序中,使用多进程有明显的优势。
多线程编程
在开始正式编成之前,首先了解几个基本的函数:
复制部分英文原文介绍,以便深入理解函数的时候用到,
Linux manual page:
SYNOPSIS:
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); Compile and link with -pthread.
使用时需要添加pthread.h头文件,同时编译的时候需要指定-pthread
DESCRIPTION
The pthread_create() function starts a new thread in the calling process. The new thread starts execution by invoking start_routine(); arg is passed as the sole argument of start_routine().
Before returning, a successful call to pthread_create() stores the ID of the new thread in the buffer pointed to by thread; this identifier is used to refer to the thread in subsequent calls to other pthreads functions.
The attr argument points to a pthread_attr_t structure whose contents are used at thread creation time to determine attributes for the new thread; this structure is initialized using pthread_attr_init(3) and related functions. If attr is NULL, then the thread is created with default attributes.
参数简介:
thread:用来存储线程的ID,在主程序里面可以使用,所以这个变量不能是局部变量,而是一个指针变量。
attr:指向一个结构体的指针,这个结构体可以定义创建函数的一些参数,如果使用NULL的话,这个线程会以默认的参数创建(Joinable).
strat_routine:第三个参数是一个函数指针,这个函数需要返回void*类型。
arg:传递给函数的参数。
NOTES
See pthread_self(3) for further information on the thread ID returned in *thread by pthread_create(). Unless real-time scheduling policies are being employed, after a call to pthread_create(), it is indeterminate which thread—the caller or the new thread—will next execute. A thread may either be joinable or detached. If a thread is joinable, then another thread can call pthread_join(3) to wait for the thread to terminate and fetch its exit status. Only when a terminated joinable thread has been joined are the last of its resources released back to the system. When a detached thread terminates, its resources are automatically released back to the system: it is not possible to join with the thread in order to obtain its exit status. Making a thread detached is useful for some types of daemon threads whose exit status the application does not need to care about. By default, a new thread is created in a joinable state, unless attr was set to create the thread in a detached state (using pthread_attr_setdetachstate(3)). Under the NPTL threading implementation, if the RLIMIT_STACK soft resource limit at the time the program started has any value other than "unlimited", then it determines the default stack size of new threads. Using pthread_attr_setstacksize(3), the stack size attribute can be explicitly set in the attr argument used to create a thread, in order to obtain a stack size other than the default. If the RLIMIT_STACK resource limit is set to "unlimited", a per- architecture value is used for the stack size. Here is the value for a few architectures:
┌─────────────┬────────────────────┐ │Architecture │ Default stack size │ ├─────────────┼────────────────────┤ │i386 │ 2 MB │ ├─────────────┼────────────────────┤ │IA-64 │ 32 MB │ ├─────────────┼────────────────────┤ │PowerPC │ 4 MB │ ├─────────────┼────────────────────┤ │S/390 │ 2 MB │ ├─────────────┼────────────────────┤ │Sparc-32 │ 2 MB │ ├─────────────┼────────────────────┤ │Sparc-64 │ 4 MB │ ├─────────────┼────────────────────┤ │x86_64 │ 2 MB │ └─────────────┴────────────────────┘
以上是函数的一些详细介绍,以及函数特性。
DEMO
下面由实例运用一下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *print_message (void *msg);
int main()
{
int tmp1, tmp2;
void *retval;
pthread_t thread1, thread2;
char message1[] = "This is thread1";
char message2[] = "This is thread2";
int ret_thread1, ret_thread2;
ret_thread1 = pthread_create(&thread1, NULL, print_message, (void *) message1);
ret_thread2 = pthread_create(&thread2, NULL, print_message, (void *) message2);
// 线程创建成功,返回0,失败返回失败号
if (ret_thread1 != 0) {
printf("线程1创建失败\n");
} else {
printf("线程1创建成功\n");
}
if (ret_thread2 != 0) {
printf("线程2创建失败\n");
} else {
printf("线程2创建成功\n");
}
//同样,pthread_join的返回值成功为0
tmp1 = pthread_join(thread1, &retval);
//printf("thread1 return value(retval) is %d\n", (int*)retval);
printf("thread1 return value(tmp) is %d\n", tmp1);
if (tmp1 != 0) {
printf("cannot join with thread1\n");
}
printf("thread1 end\n");
tmp2 = pthread_join(thread2, &retval);
//printf("thread2 return value(retval) is %d\n", (int*)retval);
printf("thread2 return value(tmp) is %d\n", tmp1);
if (tmp2 != 0) {
printf("cannot join with thread2\n");
}
printf("thread2 end\n");
}
void *print_message( void *msg ) {
int i = 0;
for (i; i<5; i++) {
printf("%s:%d\n", (char *)msg, i);
}
}
需要注意的几点:
1 pthread_create函数调用的函数应该是一个返回void*类型的。
2 函数执行成功会返回0,其余的错误都不为0.