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头文件。函数第一个参数中restrictC99标准增加的一个关键字,作用是限制指针。使用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_threadterm_threadmid线程不断等待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()函数。程序第4044行不断打印线程等待的次数,间隔时间为2秒。程序第5355行首先检查线程ID是否有效,如果有效则调用pthread_cancel()函数结束指定的线程。程序运行结果如下:

 

mid thread created!

waitting term thread 0 times!

term thread created!

waitting term thread 1 times!

 

mid线程等待2秒后,被term线程结束,整个程序退出。主线程没有打印等待错误,表示等待两个线程状态正确。

 

原创粉丝点击