《linux 下多线程编程 ( C 语言编程)》由会员分享,可在线阅读,更多相关《linux 下多线程编程 ( C 语言编程)(36页珍藏版)》请在金锄头文库上搜索。
1、linuxlinux 下下 C C 语言多线程编程实例语言多线程编程实例 作者: infobillows 发表日期: 2007-01-15 16:34 点击数: 31201 学东西,往往实例才是最让人感兴趣的,老是学基础理论,不动手,感觉没有成就感,呵呵。 下面先来一个实例。我们通过创建两个线程来实现对一个数的递加。 或许这个实例没有实际运用的价值,但是稍微改动一下,我们就可以用到其他地方去拉。 下面是我们的代码: /*thread_example.c : c multiple thread programming in linux *author : falcon *E-mail : */
2、 #include #include #include #include #define MAX 10 pthread_t thread2; pthread_mutex_t mut; int number=0, i; void *thread1() printf (“thread1 : Im thread 1n“); for (i = 0; i #include void thread(void) int i; for(i=0;i pthread_attr_t attr; pthread_t tid; /*初始化属性值,均设为默认值*/ pthread_attr_init( pthread_a
3、ttr_setscope( pthread_create( 线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采 用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的 线程结束。 只有当 pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资 源。而分离线程不是这样子的,它没有被其他的线 程所等待,自己运行结束了,线程也就 终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程 分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int
4、 detachstate)。第二个参数可选为 PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程, 而这个线程运行又非常快,它很可能在 pthread_create 函数返回之前就终止了,它终止以 后就可能将线程号和系统资源移交给其他的线程使用, 这样调用 pthread_create 的 线程就 得到了错误的线程号。 要避免这种情况可以采取一定的同步措施, 最简单的方法之一是可以 在被创建的线程里调用 pthread_cond_timewait 函数,让这个线程等待一会儿
5、,留出足够 的时间让函数 pthread_create 返回。设置一段等待时间,是 在多线程编程里常用的方法。 但是注意不要使用诸如 wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同 步的问题。 另外一个可能常 用的属性是线程的优先级,它存放在结构 sched_param 中。用函数 pthread_attr_getschedparam 和函数 pthread_attr_setschedparam 进行存放,一般说来, 我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。 #include #include pthread_attr_t attr; pthre
6、ad_t tid; sched_param param; int newprio=20; pthread_attr_init( pthread_attr_getschedparam( param.sched_priority=newprio; pthread_attr_setschedparam( pthread_create( 4 4 线程的数据处理线程的数据处理 和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数 据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我 们必须当心 有多个不同的进程访问相同的变量。 许多函数是不可重入的, 即同时不能运行
7、一个函数的多 个拷贝(除非使用不同的数据段)。在函数中声明的静态变量 常常带来问题,函数的返回 值也会有问题。 因为如果返回的是函数内部静态声明的空间的地址, 则在一个线程调用该函 数得到地址后使用该地址指向的数据时,别的 线程可能调用此函数并修改了这一段数据。 在进程中共享的变量必须用关键字 volatile 来定义, 这是为了防止编译器在优化时 (如 gcc 中使用-OX 参 数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方 法来保证我们对变量的正确使用。下面,我们就逐步介绍处理线程数据时的有关知识。 4.1 4.1 线程数据线程数据 在单线程的程序里,有两种基本的数据:
8、全局变量和局部变量。但在多线程程序里, 还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在 线程内部, 各个函数可以象使用全局变量一样调用它, 但它对线程外部的其它线程是不可见 的。这种数据的必要性是显而易见 的。例如我们常见的变量 errno,它返回标准的出错信 息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局 变量,否则在 A 线程里输出的很可能是 B 线程的出错信息。要实现诸如此类的变量,我们 就必须使用线程数据。 我们为每个线程数据创建一个键, 它和这个键相关联, 在各个线程 里, 都使用这个键来指代
9、线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个 线程里,它代表同样的数据内容。 和线程数据相关的函数主要有 4 个:创建一个键;为一个键指定线程数据;从一个键读 取线程数据;删除键。 创建键的函数原型为: extern int pthread_key_create _P (pthread_key_t *_key, void (*_destr_function) (void *); 第一个参数为指向一个键值的指针, 第二个参数指明了一个 destructor 函数, 如果这 个参数不为空,那么当每个线程结束时,系统将调用这个函数来释 放绑定在这个键上的内 存块。这个函数常和函数
10、pthread_once (pthread_once_t*once_control, void (*initroutine) (void)一起使用,为了让这个键只被创建一次。函数 pthread_once 声明 一个初始化函数,第一次调用 pthread_once 时它执行这 个函数,以后的调用将被它忽略。 在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函数 createWindow,这个函数定义一个图形窗口(数据类型为 Fl_Window *,这是图形界面开发 工具 FLTK 中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。 /* 声明一个键*/
11、pthread_key_t myWinKey; /* 函数 createWindow */ void createWindow ( void ) Fl_Window * win; static pthread_once_t once= PTHREAD_ONCE_INIT; /* 调用函数 createMyKey,创建键*/ pthread_once ( /*win 指向一个新建立的窗口*/ win=new Fl_Window( 0, 0, 100, 100, “MyWindow“); /* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/ setWindow(win); /* 将窗口指针
12、值绑定在键 myWinKey 上*/ pthread_setpecific ( myWinKey, win); /* 函数 createMyKey,创建一个键,并指定了 destructor */ void createMyKey ( void ) pthread_keycreate( /* 函数 freeWinKey,释放空间*/ void freeWinKey ( Fl_Window * win) delete win; 这样,在不同的线程中调用函数 createMyWin,都可以得到在线程内部均可见的窗口 变量,这个变量通过函数 pthread_getspecific 得到。在上面的例子
13、中,我们已经使用了 函数 pthread_setspecific 来将线程数据和一个键绑定在一 起。这两个函数的原型如下: extern int pthread_setspecific _P (pthread_key_t _key,_const void *_pointer); extern void *pthread_getspecific _P (pthread_key_t _key); 这两个函数的参数意义和使用方法是显而易见的。要注意的是,用 pthread_setspecific 为一个键指定新的线程数据时,必须自己释放原有的 线程数据以回 收空间。 这个过程函数 pthread_k
14、ey_delete 用来删除一个键, 这个键占用的内存将被释放, 但同样要注意的是,它只释放键占用的 内存,并不释放该键关联的线程数据所占用的内存 资源,而且它也不会触发函数 pthread_key_create 中定义的 destructor 函数。线程 数据 的释放必须在释放键之前完成。 4.2 4.2 互斥锁互斥锁 互斥锁用来保证一段时间内只有一个线程在执行一段代码。 必要性显而易见: 假设各个 线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。 我们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且我们假定一 个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息
15、或没有信息。 void reader_function ( void ); void writer_function ( void ); char buffer; int buffer_has_item=0; pthread_mutex_t mutex; struct timespec delay; void main ( void ) pthread_t reader; /* 定义延迟时间*/ delay.tv_sec = 2; delay.tv_nec = 0; /* 用默认属性初始化一个互斥锁对象*/ pthread_mutex_init ( pthread_create( writer
16、_function( ); void writer_function (void) while(1) /* 锁定互斥锁*/ pthread_mutex_lock ( if (buffer_has_item=0) buffer=make_new_item( ); buffer_has_item=1; /* 打开互斥锁*/ pthread_mutex_unlock( pthread_delay_np( void reader_function(void) while(1) pthread_mutex_lock( if(buffer_has_item=1) consume_item(buffer); buffer_has_item=0;