8.2 多线程开发
来源:互联网 发布:阿里云出售域名 编辑:程序博客网 时间:2024/05/21 01:44
8.2.1 线程的概念
线程是一种轻量级的进程。与进程最大的不同是线程没有系统资源。线程是操作系统调度的最小单位,可以理解为一个进程是由一个或者多个线程组成的。在操作系统内核中,是按照线程作为调度单位来调度资源的。在一个进程内部,多个线程之间的资源是共享的。也就是说,如果一个进程内部的所有线程拥有相同的代码地址空间和数据空间,则任意一个线程都可以访问其他线程的数据。
8.2.2 进程和线程对比
进程和线程有许多相似之处,但是也有许多不同:
q 资源分配不同。从线程和进程的定义可以看出,进程拥有独立的内存和系统资源,而在一个进程内部,线程之间的资源是共享的,系统不会为线程分配系统资源。
q 工作效率不同。进程拥有系统资源,在进程切换的时候,操作系统需要保留进程占用的资源;而线程的切换不需要保留系统资源,切换效率远高于进程。线程较高的切换效率提高了数据处理的并发能力。
q 执行方式不同。线程有程序运行的入口地址,但是线程不能独立运行。由于线程不占有系统资源,所以线程必须存放在进程中。进程可以被操作系统直接调度。在一个进程内部的多个线程可以共享资源和调度,不同进程之间的线程资源是不能直接共享的。进程可以被认为是线程的集合。
图8-4展示了进程和线程的对比,进程1和进程2拥有各自独立的代码、静态数据、堆栈和寄存器等资源,进程之间是相互独立的。线程不具备自己独立的资源,仅具备必要的堆栈和寄存器、从逻辑上看,线程是进程内部的一个实体,在一个进程内,各线程共享代码和数据。
存储器 存储器
图8-4 进程和线程对比
8.2.3 创建线程
Linux系统开发多线程程序大多使用pthread库,pthread库是符合POSIX线程标准的一个应用库,提供了线程的管理和操作方法。pthread库对线程操作的函数基本都以pthread开头,创建线程的函数定义如下:
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);
使用pthread库需要包含pthread.h头文件。函数第一个参数中restrict是C99标准增加的一个关键字,作用是限制指针。使用restrict关键字修饰的指针所指向的数据是唯一的。换句话说,使用restrict关键字修饰一个指针后,指针所指向的数据仅能被该指针所用,其他的指针无法再使用这块数据。
pthread_create()函数的参数thread返回创建线程的ID。参数attr是一个pthread_attr_t类型的结构,用来设置线程的属性,如果没有特殊的要求,置为NULL即可;参数start_routine是一个函数指针,指向了一个函数,这个函数就是线程要运行的代码;arg参数是start_rouine指向的函数传入的参数,当执行用户的线程函数时,会把arg带的参数传入。如果创建线程成功则返回0,如果失败则返回错误号。实例8-8给出了一个创建线程的代码。
实例8-8 使用pthread库创建线程
1 #include <pthread.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 void* thread_func(void *arg) // 线程函数
6 {
7 int *val = arg;
8 printf("Hi, I'm a thread!/n");
9 if (NULL!=arg) // 如果参数不为空,打印参数内容
10 printf("argument set: %d/n", *val);
11 }
12
13 int main()
14 {
15 pthread_t tid; // 线程ID
16 int t_arg = 100; // 给线程传入的参数值
17
18 if (pthread_create(&tid, NULL, thread_func, &t_arg)) // 创建线程
19 perror("Fail to create thread");
20
21 sleep(1); // 睡眠1秒,等待线程执行
22 printf("Main thread!/n");
23
24 return 0;
25 }
26
在程序第15行中定义了pthread_t类型的变量tid用来保存创建成功的线程ID,程序第5行定义了一个线程函数,新创建的线程会执行线程函数内部的代码。程序第18行使用pthead_create()函数创建一个线程,并且给线程传递一个参数。在程序的第9行,也就是线程函数内部,会检查线程参数是否存在,如果存在,会打印参数的内容。第21行使用sleep()函数让主线程暂停一下,等待新创建线程结束,这样做是因为如果不等待,有可能由于主线程运行速度过快,在其他线程结束之前结束,导致整个程序退出。程序的输出结果如下:
Hi, I'm a thread!
argument set: 100
Main thread!
读者在编译这个程序的时候,使用gcc t_create.c可能会报错,报错信息如下:
/tmp/ccC8EJ0O.o(.text+0x6d): In function `main':
t_create.c: undefined reference to `pthread_create'
collect2: ld 返回 1
这是因为,pthread库不是Linux的标准库,需要给编译器制定连接的库,使用gcc t_create.c –lpthread命令,编译器会寻找libpthread.a静态库文件,并且连接到用户代码。
8.2.4 取消线程
线程的退出有几种条件,当线程本身的代码运行结束后,会自动退出;或者线程代码中调用return也会导致线程退出;还有一种情况是通过其他的线程把一个线程退出,pthread库提供了pthread_cancel()函数用来取消一个线程的执行。函数定义如下:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
参数thread是要取消的线程ID,取消成功函数返回0,失败返回出错代码。下面的实例8-9演示了使用pthread_cancel()函数取消一个线程。
实例8-9 取消线程实例
1 #include <pthread.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 void* thread_func(void *arg) // 线程函数
6 {
7 int *val = arg;
8 printf("Hi, I'm a thread!/n");
9 if (NULL!=arg) { // 如果参数不为空,打印参数内容
10 while(1)
11 printf("argument set: %d/n", *val);
12 }
13 }
14
15 int main()
16 {
17 pthread_t tid; // 线程ID
18 int t_arg = 100; // 给线程传入的参数值
19
20 if (pthread_create(&tid, NULL, thread_func, &t_arg)) // 创建线程
21 perror("Fail to create thread");
22
23 sleep(1); // 睡眠1秒,等待线程执行
24 printf("Main thread!/n");
25 pthread_cancel(tid); // 取消线程
26
27 return 0;
28 }
29
程序在函数thread_func()内,对参数判断成功后加入了一个死循环,程序的第10行和第11行会不断打印出参数的值,程序第25行增加了取消线程的操作,程序运行结果如下:
Hi, I'm a thread!
argument set: 100
{打印若干次参数值}
Main thread!
程序创建线程后,会不断打印线程收到的参数。主线程在等待1秒后,调用pthread_cancel()函数取消了线程,之后主线程也运行结束,程序退出。
8.2.5 等待线程
在线程操作实例中,主线程使用sleep()函数暂停自己的运行,等待新创建的线程结束。使用延迟函数的方法在简单的程序中还能对付,但是复杂一点的程序就不好用了。由于线程的运行时间不确定,导致程序的运行结果无法预测。pthread库提供了一种等待其他线程结束的方法,使用pthread_join()函数等待一个线程结束,函数定义如下:
#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);
参数thread是要等待线程的ID,参数value_ptr指向的是退出线程的返回值。如果被等待线程成功返回,函数返回0,其他情况返回出错代码。
8.2.6 使用pthread库线程操作实例
本节最后给出一个多线程操作实例,在主程序中创建两个线程mid_thread和term_thread,mid线程不断等待term线程终止它,并且每隔2秒打印一次等待的次数。term接收从主函数传进来的mid线程的ID。如果线程ID合法,就调用pthread_cancel()函数结束mid线程。程序代码如下:
实例8-10 pthread库线程操作实例
1 // pthread_demo.c
2 #include <pthread.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 void* mid_thread(void *arg); // mid线程声明
8 void* term_thread(void *arg); // term线程声明
9
10 int main()
11 {
12 pthread_t mid_tid, term_tid; // 存放线程ID
13
14 if (pthread_create(&mid_tid, NULL, mid_thread, NULL)) {
// 创建mid线程
15 perror("Create mid thread error!");
16 return 0;
17 }
18
19 if (pthread_create(&term_tid, NULL, term_thread, (void*)&mid_thread)) { // 创建term线程
20 perror("Create term thread fail!/n");
21 return 0;
22 }
23
24 if (pthread_join(mid_tid, NULL)) { // 等待mid线程结束
25 perror("wait mid thread error!");
26 return 0;
27 }
28
29 if (pthread_join(term_tid, NULL)) { // 等待term线程结束
30 perror("wait term thread error!");
31 return 0;
32 }
33
34 return 0;
35 }
36
37 void* mid_thread(void *arg) // mid线程定义
38 {
39 int times = 0;
40 printf("mid thread created!/n");
41 while(1) { // 不断打印等待的次数,间隔2秒
42 printf("waitting term thread %d times!/n", times);
43 sleep(2);
44 times++;
45 }
46 }
47
48 void* term_thread(void *arg) // term线程定义
49 {
50 pthread_t *tid;
51 printf("term thread created!/n");
52 sleep(2);
53 if (NULL!=arg) {
54 tid = (pthread_t*)arg;
55 pthread_cancel((*tid)); // 如果线程ID合法,结束线程
56 }
57 }
程序定义了两个线程函数mid_thread()和term_thread()。第14行和第19行调用pthread_create()函数创建两个线程,第19行创建线程的时候还需要把mid线程的ID传入term_thread()函数。程序第40~44行不断打印线程等待的次数,间隔时间为2秒。程序第53~55行首先检查线程ID是否有效,如果有效则调用pthread_cancel()函数结束指定的线程。程序运行结果如下:
mid thread created!
waitting term thread 0 times!
term thread created!
waitting term thread 1 times!
mid线程等待2秒后,被term线程结束,整个程序退出。主线程没有打印等待错误,表示等待两个线程状态正确。
- 8.2 多线程开发
- 8.2 多线程开发
- 多线程开发
- 多线程开发
- 多线程开发
- 多线程开发
- 多线程开发
- 多线程开发
- 多线程开发
- 多线程开发
- 多线程开发
- ios 开发系列--多线程开发
- 【iOS开发系列】多线程开发
- 什么是多线程开发?
- vc多线程开发(1)
- Symbian多线程开发问题
- java多线程开发基础
- Windows Mobile 多线程开发
- 短时记忆与网页可用性
- 8.2 多线程开发
- 李眈:Moblin让用户拥有真正互联网体验
- vs2008编译的debug版本程序在没有装vs2008的机器上启动失败问题
- 戴尔将英特尔Moblin引入10v上网本
- 8.2 多线程开发
- 为什么编程者总是高估自己低估别人
- realview MDK C/C++ 混合编程问题
- 考研经验交流
- 仿Google自动提示 SearchSuggess
- GOOGLE卫星地图 计算方式
- 无法初始化链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.Jet.OLEDB.4.0" 的数据源对象
- apache转发实现iis和apache共享80端口
- 《做单》读后启示1--关于资源整合能力