Linux下C语言多线程编程(线程简介)

上传人:ji****72 文档编号:37521982 上传时间:2018-04-17 格式:DOCX 页数:20 大小:39.02KB
返回 下载 相关 举报
Linux下C语言多线程编程(线程简介)_第1页
第1页 / 共20页
Linux下C语言多线程编程(线程简介)_第2页
第2页 / 共20页
Linux下C语言多线程编程(线程简介)_第3页
第3页 / 共20页
Linux下C语言多线程编程(线程简介)_第4页
第4页 / 共20页
Linux下C语言多线程编程(线程简介)_第5页
第5页 / 共20页
点击查看更多>>
资源描述

《Linux下C语言多线程编程(线程简介)》由会员分享,可在线阅读,更多相关《Linux下C语言多线程编程(线程简介)(20页珍藏版)》请在金锄头文库上搜索。

1、Linux 下下 c 语言多线程编程语言多线程编程引言线程(thread)技术早在 60 年代就被提出,但真正应用多线程到操作系统中去,是在 80 年代中期,solaris 是这方面的佼佼者。传统的 Unix 也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就意味着多进程。现在,多为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。使用多线程的理由之一是和进程相比,它是一种非常“节俭“的多任务操作方式。我们知道,在 Linux 系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表

2、来维护它的代码段、堆栈段和数据段,这是一种“昂贵“的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程

3、所修改,有的子程序中声明为 static 的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。 2) 使多 CPU 系统更加有效。操作系统会保证当线程数不大于 CPU 数目时,不同的线程运行于不同的 CPU 上。 3) 改

4、善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。下面我们先来尝试编写一个简单的多线程程序。简单的多线程编程简单的多线程编程 Linux 系统下的多线程遵循 POSIX 线程接口,称为 pthread。编写 Linux 下的多线程程序,需要使用头文件 pthread.h,连接时需要使用库 libpthread.a。顺便说一下,Linux 下 pthread 的实现是通过系统调用 clone()来实现的。clone()是 Linux 所特有的系统调用,它的使用方式类似 fork,关于 clone()的详细情况,有兴趣的读者可以去查看

5、有关文档说明。下面我们展示一个最简单的多线程程序 example1.c。/* example.c*/ #include #include void thread(void) int i; for(i=0;i pthread_attr_t attr; pthread_t tid; /*初始化属性值,均设为默认值*/ pthread_attr_init( pthread_attr_setscope( pthread_create( 线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当 pthr

6、ead_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为 PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快

7、,它很可能在 pthread_create 函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create 的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait 函数,让这个线程等待一会儿,留出足够的时间让函数 pthread_create 返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如 wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。另外一个可能常用的属性是线程的优先级,它存放在结构 sche

8、d_param 中。用函数pthread_attr_getschedparam 和函数 pthread_attr_setschedparam 进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。#include #include pthread_attr_t attr; pthread_t tid; sched_param param; int newprio=20; pthread_attr_init( pthread_attr_getschedparam( param.sched_priority=newprio; pthread_attr_sets

9、chedparam( pthread_create( 线程的数据处理线程的数据处理和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字 volatile

10、来定义,这是为了防止编译器在优化时(如 gcc 中使用-OX 参数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。下面,我们就逐步介绍处理线程数据时的有关知识。 4.1 线程数据线程数据在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见的。例如我们常见的变量 errno,它返回标准的出错信息。它显然不能是一个局部变

11、量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A 线程里输出的很可能是 B 线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。和线程数据相关的函数主要有 4 个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。 创建键的函数原型为: extern int pthread_key_create _P (pthread_key_t *_key, void (*_destr_fun

12、ction) (void *);第一个参数为指向一个键值的指针,第二个参数指明了一个 destructor 函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数 pthread_once (pthread_once_t*once_control, void (*initroutine) (void)一起使用,为了让这个键只被创建一次。函数 pthread_once 声明一个初始化函数,第一次调用 pthread_once 时它执行这个函数,以后的调用将被它忽略。 在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函

13、数createWindow,这个函数定义一个图形窗口(数据类型为 Fl_Window *,这是图形界面开发工具 FLTK 中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。/* 声明一个键*/ pthread_key_t myWinKey; /* 函数 createWindow */ void createWindow ( void ) Fl_Window * win; static pthread_once_t once= PTHREAD_ONCE_INIT; /* 调用函数 createMyKey,创建键*/ pthread_once ( /*win 指向一个新建立的窗口

14、*/ win=new Fl_Window( 0, 0, 100, 100, “MyWindow“); /* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/ setWindow(win); /* 将窗口指针值绑定在键 myWinKey 上*/ pthread_setpecific ( myWinKey, win); /* 函数 createMyKey,创建一个键,并指定了 destructor */ void createMyKey ( void ) pthread_keycreate( /* 函数 freeWinKey,释放空间*/ void freeWinKey ( Fl_Windo

15、w * win) delete win; 这样,在不同的线程中调用函数 createMyWin,都可以得到在线程内部均可见的窗口变量,这个变量通过函数 pthread_getspecific 得到。在上面的例子中,我们已经使用了函数 pthread_setspecific 来将线程数据和一个键绑定在一起。这两个函数的原型如下:extern int pthread_setspecific _P (pthread_key_t _key,_const void *_pointer); extern void *pthread_getspecific _P (pthread_key_t _key);这

16、两个函数的参数意义和使用方法是显而易见的。要注意的是,用pthread_setspecific 为一个键指定新的线程数据时,必须自己释放原有的线程数据以回收空间。这个过程函数 pthread_key_delete 用来删除一个键,这个键占用的内存将被释放,但同样要注意的是,它只释放键占用的内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不会触发函数 pthread_key_create 中定义的 destructor 函数。线程数据的释放必须在释放键之前完成。4.2 互斥锁互斥锁互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。我们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。void reader_function ( void ); void writer_function ( void ); char buffer; in

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号