Presentation is loading. Please wait.

Presentation is loading. Please wait.

© 2005 博创科技 北 京 博 创 兴 业 科 技 有 限 公 司 BEIJNG UNIVERSAL PIONEERING TECHNOLOGY Co., LTD 博创科技 嵌入互动 第四讲 Linux 多线程编程 北京博创兴业科技有限公司.

Similar presentations


Presentation on theme: "© 2005 博创科技 北 京 博 创 兴 业 科 技 有 限 公 司 BEIJNG UNIVERSAL PIONEERING TECHNOLOGY Co., LTD 博创科技 嵌入互动 第四讲 Linux 多线程编程 北京博创兴业科技有限公司."— Presentation transcript:

1 © 2005 博创科技 北 京 博 创 兴 业 科 技 有 限 公 司 BEIJNG UNIVERSAL PIONEERING TECHNOLOGY Co., LTD 博创科技 嵌入互动 第四讲 Linux 多线程编程 北京博创兴业科技有限公司

2 博创科技 嵌入互动 © 2005 博创科技 Linux 多线程编程 主讲人:王君 wju_uptech@126.com

3 博创科技 嵌入互动 © 2006 博创科技 进程与线程  使用多线程的理由之一是和进程相比,它是一种非常 " 节俭 " 的 多任务操作方式。  在 Linux 系统下,启动一个新的进程必须分配给它独立的地址 空间,建立众多的数据表来维护它的代码段、堆栈段和数据 段,这是一种 " 昂贵 " 的多任务工作方式。  而运行于一个进程中的多个线程,它们彼此之间使用相同的 地址空间,共享大部分数据,启动一个线程所花费的空间远 远小于启动一个进程所花费的空间,而且,线程间彼此切换 所需的时间也远远小于进程间切换所需要的时间。

4 博创科技 嵌入互动 © 2006 博创科技 进程与线程(二)  使用多线程的理由之二是线程间方便的通信机制。  不同的进程具有独立的数据空间,要进行数据的传 递只能通过通信的方式进行  同一进程下的线程之间共享数据空间,所以一个线 程的数据可以直接为其它线程所用

5 博创科技 嵌入互动 © 2006 博创科技 多线程程序的优点  提高应用程序响应。当一个操作耗时很长时,整个 系统都会等待这个操作,多线程技术会将耗时长的 操作( time consuming )置于一个新的线程。  使多 CPU 系统更加有效。操作系统会保证当线程数 不大于 CPU 数目时,不同的线程运行于不同的 CPU 上。  改善程序结构。一个既长又复杂的进程可以考虑分 为多个线程,成为几个独立或半独立的运行部分。

6 博创科技 嵌入互动 © 2006 博创科技 多线程编程起步  编写 Linux 下的多线程程序,需要使用头文件 pthread.h ,连接时需要使用库 libpthread.a  多线程程序实例  /* example.c*/ #include #include void thread(void) { int i; for(i=0;i<3;i++) printf("This is a pthread.\n"); }

7 博创科技 嵌入互动 © 2006 博创科技  int main(void) { pthread_t id; int i,ret; ret=pthread_create(&id,NULL,(void *) thread,NULL); if(ret!=0){ printf ("Create pthread error!\n"); exit (1); } for(i=0;i<3;i++) printf("This is the main process.\n"); pthread_join(id,NULL); return (0); }

8 博创科技 嵌入互动 © 2006 博创科技  执行: gcc example.c -lpthread -o example  -l 参数用于指定编译时要用到的库  运行生成的 example  每次运行的结果可能不同,这是因为两个线程在争 夺 CPU 资源

9 博创科技 嵌入互动 © 2006 博创科技 线程标识符 pthread_t  pthread_t 在头文件 /usr/include/bits/pthreadtypes.h 中定义: typedef unsigned long int pthread_t;  用来标识一个线程

10 博创科技 嵌入互动 © 2006 博创科技 主要 API 函数介绍  LIBC 中的 pthread 库提供了大量的 API 函数  $ cd /usr/lib

11 博创科技 嵌入互动 © 2006 博创科技 线程创建函数  函数原型: int pthread_create (pthread_t * thread_id , __const pthread_attr_t * __attr, void *(*__start_routine) (void *),void *__restrict __arg)  第一个参数为指向线程标识符的指针  第二个参数用来设置线程属性  第三个参数是线程运行函数的起始地址  最后一个参数是运行函数的参数  函数 thread 不需要参数,所以最后一个参数设为空指针。 第二个参数也设为空指针,这样将生成默认属性的线程。

12 博创科技 嵌入互动 © 2006 博创科技  当创建线程成功时,函数返回 0 ,若不为 0 则说明创 建线程失败,常见的错误返回代码为 EAGAIN 和 EINVAL 。前者表示系统限制创建新的线程,例如线 程数目过多了;后者表示第二个参数代表的线程属 性值非法。  创建线程成功后,新创建的线程则运行参数三和参 数四确定的函数,原来的线程则继续运行下一行代 码。

13 博创科技 嵌入互动 © 2006 博创科技 pthread_join 函数  用来等待一个线程的结束。  函数原型: int pthread_join (pthread_t __th, void **__thread_return)  第一个参数为被等待的线程标识符 。  第二个参数为一个用户定义的指针,用来存储被等 待线程返回值。  这个函数是一个线程阻塞的函数,调用它的函数将 一直等待到被等待的线程结束为止,当函数返回时, 被等待线程的资源被收回。

14 博创科技 嵌入互动 © 2006 博创科技 pthread_exit 函数  一个线程的结束有两种途径,一种是象我们上面的 例子,函数结束了,调用它的线程也就结束了;另 一种方式是通过函数 pthread_exit 实现 。  函数原型: void pthread_exit (void *__retval )  唯一的参数是函数的返回代码 。如果 pthread_join 中 的第二个参数 thread_return 不是 NULL ,这个值将被 传递给 thread_return 。  需要注意的是:一个线程不能被多个线程等待,否 则第一个接收到信号的线程成功返回,其余调用 pthread_join 的线程则返回错误代码 ESRCH 。

15 博创科技 嵌入互动 © 2006 博创科技 线程的属性  使用 pthread_create 函数创建线程时,线程参数一般 都为默认值,即将第二个参数设为 NULL ,对大多数 程序来说,使用默认属性就够了  属性结构为 pthread_attr_t ,它在头文件 /usr/include/pthread.h 中定义,属性值不能直接设置, 须使用相关函数进行操作,初始化的函数为 pthread_attr_init ,这个函数必须在 pthread_create 函 数之前调用。

16 博创科技 嵌入互动 © 2006 博创科技 线程的优先级  线程的优先级是线程的常用属性,  它存放在结构 sched_param 中。用函数 pthread_attr_getschedparam 和函数 pthread_attr_setschedparam 进行存放,一般是先取 优先级,对取得的值修改后再存放回去。  下面是一个简单的例子:

17 博创科技 嵌入互动 © 2006 博创科技  #include #include pthread_attr_t attr; pthread_t tid; sched_param param; int newprio=20; pthread_attr_init(&attr); pthread_attr_getschedparam(&attr, &param); param.sched_priority=newprio; pthread_attr_setschedparam(&attr, &param); pthread_create(&tid, &attr, (void *)myfunction, myarg);

18 博创科技 嵌入互动 © 2006 博创科技 互斥锁  互斥锁用来保证一段时间内只有一个线程在执行一 段代码  看下面一段代码。这是一个读 / 写程序,它们公用一 个缓冲区,并且假定一个缓冲区只能保存一条信息。 即缓冲区只有两个状态:有信息或没有信息。

19 博创科技 嵌入互动 © 2006 博创科技  void reader_function ( void ); void writer_function ( void ); char buffer; int buffer_has_item=0; pthread_mutex_t mutex; struct timespec delay;

20 博创科技 嵌入互动 © 2006 博创科技  void main ( void ){ pthread_t reader; /* 定义延迟时间 */ delay.tv_sec = 2; delay.tv_nec = 0; /* 用默认属性初始化一个互斥锁对象 */ pthread_mutex_init (&mutex,NULL); pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL); writer_function( ); }

21 博创科技 嵌入互动 © 2006 博创科技  void writer_function (void){ while(1){ /* 锁定互斥锁 */ pthread_mutex_lock (&mutex); if (buffer_has_item==0){ buffer=make_new_item( ); buffer_has_item=1; } /* 打开互斥锁 */ pthread_mutex_unlock(&mutex); pthread_delay_np(&delay); } }

22 博创科技 嵌入互动 © 2006 博创科技  void reader_function(void){ while(1){ pthread_mutex_lock(&mutex); if(buffer_has_item==1){ consume_item(buffer); buffer_has_item=0; } pthread_mutex_unlock(&mutex); pthread_delay_np(&delay); } }

23 博创科技 嵌入互动 © 2006 博创科技  声明互斥锁变量 pthread_mutex_t mutex  函数 pthread_mutex_init 用来生成一个互斥锁。 NULL 参数表明使用默认属性。

24 博创科技 嵌入互动 © 2006 博创科技  pthread_mutex_lock 声明开始用互斥锁上锁,此后的 代码直至调用 pthread_mutex_unlock 为止,均被上 锁,即同一时间只能被一个线程调用执行。当一个 线程执行到 pthread_mutex_lock 处时,如果该锁此时 被另一个线程使用,那此线程被阻塞,即程序将等 待到另一个线程释放此互斥锁。在上面的例子中, 我们使用了 pthread_delay_np 函数,让线程睡眠一段 时间,就是为了防止一个线程始终占据此函数。 。

25 博创科技 嵌入互动 © 2006 博创科技 条件变量  使用互斥锁可实现线程间数据的共享和通信,互斥 锁一个明显的缺点是它只有两种状态:锁定和非锁 定。而条件变量通过允许线程阻塞和等待另一个线 程发送信号的方法弥补了互斥锁的不足,它常和互 斥锁一起使用。使用时,条件变量被用来阻塞一个 线程,当条件不满足时,线程往往解开相应的互斥 锁并等待条件发生变化。一旦其它的某个线程改变 了条件变量,相应的条件变量唤醒一个或多个正被 此条件变量阻塞的线程。这些线程将重新锁定互斥 锁并重新测试条件是否满足。

26 博创科技 嵌入互动 © 2006 博创科技 pthread_cond_init 函数  条件变量的结构为 pthread_cond_t ,函数 pthread_cond_init ()被用来初始化一个条件变量。 它的原型为:  int pthread_cond_init (pthread_cond_t * cond, __const pthread_condattr_t * cond_attr)  其中 cond 是一个指向结构 pthread_cond_t 的指针  cond_attr 是一个指向结构 pthread_condattr_t 的指针。 结构 pthread_condattr_t 是条件变量的属性结构

27 博创科技 嵌入互动 © 2006 博创科技  注意:条件变量只有在未被使用时才能重新初始化 或被释放。  释放一个条件变量的函数为 pthread_cond_ destroy ( pthread_cond_t cond )。

28 博创科技 嵌入互动 © 2006 博创科技 pthread_cond_wait 函数  使线程阻塞在一个条件变量上。  函数原型: extern int pthread_cond_wait (pthread_cond_t *__restrict__cond, pthread_mutex_t *__restrict __mutex)  线程解开 mutex 指向的锁并被条件变量 cond 阻塞。线程可以 被函数 pthread_cond_signal 和函数 pthread_cond_broadcast 唤醒  但是要注意的是,条件变量只是起阻塞和唤醒线程的作用, 具体的判断条件还需用户给出,例如一个变量是否为 0 等等, 线程被唤醒后,它将重新检查判断条件是否满足,如果还不 满足,一般说来线程应该仍阻塞在这里,等待被下一次唤醒。 这个过程一般用 while 语句实现。

29 博创科技 嵌入互动 © 2006 博创科技 pthread_cond_timedwait 函数  也可以用来阻塞线程  函数原型: extern int pthread_cond_timedwait (pthread_cond_t *__cond, pthread_mutex_t *__mutex, __const struct timespec *__abstime)  它比函数 pthread_cond_wait ()多了一个时间参数, 经历 abstime 段时间后,即使条件变量不满足,阻塞 也被解除。

30 博创科技 嵌入互动 © 2006 博创科技 pthread_cond_signal 函数  用来释放被阻塞在条件变量 cond 上的一个线程。  函数原型: extern int pthread_cond_signal (pthread_cond_t *__cond)  多个线程阻塞在此条件变量上时,哪一个线程被唤 醒是由线程的调度策略所决定的。

31 博创科技 嵌入互动 © 2006 博创科技 使用函数 pthread_cond_wait ()和函数 pthread_cond_signal ()的一个简单的例子  pthread_mutex_t count_lock; pthread_cond_t count_nonzero; unsigned count; decrement_count () { pthread_mutex_lock (&count_lock); while(count==0) pthread_cond_wait( &count_nonzero, &count_lock); count=count -1; pthread_mutex_unlock (&count_lock); }

32 博创科技 嵌入互动 © 2006 博创科技  increment_count(){ pthread_mutex_lock(&count_lock); if(count==0) pthread_cond_signal(&count_nonzero); count=count+1; pthread_mutex_unlock(&count_lock); }  count 值为 0 时, decrement 函数 pthread_cond_wait 处被阻塞,并打开互斥锁 count_lock 。此时,当调用 到函数 increment_count 时, pthread_cond_signal () 函数改变条件变量,告知 decrement_count ()停止 阻塞。

33 博创科技 嵌入互动 © 2006 博创科技  下面我们分析一下著名的生产者-消费者问题模型 的实现,主程序中分别启动生产者线程和消费者线 程。生产者线程不断顺序地将 0 到 1000 的数字写入共 享的循环缓冲区,同时消费者线程不断地从共享的 循环缓冲区读取数据。

34 博创科技 嵌入互动 © 2006 博创科技  产者写入缓冲区和消费者从缓冲区读数的具体流程:  生产者首先要获得互斥锁,并且判断写指针 +1 后是否等 于读指针,如果相等则进入等待状态,等候条件变 notfull ; 如果不等则向缓冲区中写一个整数,并且设置条件变量为 notempty ,最后释放互斥锁。消费者线程与生产者线程 类似 。

35 博创科技 嵌入互动 © 2006 博创科技


Download ppt "© 2005 博创科技 北 京 博 创 兴 业 科 技 有 限 公 司 BEIJNG UNIVERSAL PIONEERING TECHNOLOGY Co., LTD 博创科技 嵌入互动 第四讲 Linux 多线程编程 北京博创兴业科技有限公司."

Similar presentations


Ads by Google