Linux下的多线程编程上机学习材料.doc_第1页
Linux下的多线程编程上机学习材料.doc_第2页
Linux下的多线程编程上机学习材料.doc_第3页
Linux下的多线程编程上机学习材料.doc_第4页
Linux下的多线程编程上机学习材料.doc_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux下的多线程编程上机学习Linux下的多线程编程上机准备吴雨龙目录1.我们为什么要练习多线程?(多线程的好处)22.Linux下的多线程编程必备知识21)VIM的使用3a)指令模式下常用命令3b)插入模式32)编译器gcc的使用必知33)调试器gdb的使用44)Linux下的C编程常用头文件介绍43.Linux多线程编程相关函数介绍41)主要线程函数介绍4a)线程创建函数pthread_create()5b)线程终止函数pthread_exit()6c)控制线程同步的函数pthread_exit()62)信号量6a)sem_init()6b)sem_wait()和sem_trywait()7c)sem_post()7d)sem_getvalue()7e)sem_destroy()73)互斥锁8a)互斥锁初始化:pthread_mutex_init()8b)互斥锁上锁:pthread_mutex_lock()8c)互斥锁解锁:pthread_mutex_unlock()8d)撤销互斥锁:pthread_mutex_destroy()84.Linux下的多线程编程引例81)引例一:基本的线程程序理解Linux多线程程序的基本结构8a)源代码9b)程序解析102)引例二:两个线程的同时执行最简单的线程协调模型10a)源代码10b)程序解析123)引例三:信号量的使用借助信号量体现多线程分工合作的思想12a)源代码12b)程序解析134)引例四:使用互斥量将引例三改为用互斥量实现13a)源代码13b)程序解析155)引例五:一个简单的真正多个线程的程序真正的多线程15a)源代码15b)程序解析17c)思考一下吧18181. 我们为什么要练习多线程?(多线程的好处)1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。2. Linux下的多线程编程必备知识1) VIM的使用VIM的操作主要分为两个模式下进行,分别是“指令模式”,“插入模式”。指令模式插入模式:a键、i键插入模式指令模式:esc键a) 指令模式下常用命令在指令模式下移动光标时如果想移动多行,可以先输入要移动的行数,然后直接按动移动的方向键,就会向该方向移动指定的次数。命令功能x删除光标所在字元。dd删除光标所在的列。b移动到下个字的第一个字母。w移动到上个字的第一个字母。/string往右移动到有 string 的地方?string往左移动到有 string 的地方y复制(yank)p放置(put)c修改(change)文件指令(多以:开头)功能:q!不存储退出:wq存储退出:w存档b) 插入模式插入模式下大家就可以比较习惯的进行文本的编辑。只是移动光标时稍有不便。不过可在命令模式下用先键入指令执行次数的方法一次移动多行或多列。2) 编译器gcc的使用必知gcc的最基本格式为:$ gcc f1.c该格式将f1.c源文件使用默认设置编译。产生名为默认名a.out的可执行文件。也可自定义输出的文件名。具体方法为(后缀无要求):$ gcc f1.c -o f1.out对于多线程编程,还需要加入-lpthread选项来链接线程库。具体方法为:$ gcc f1.c -o f1.out -lpthread为了让多线程程序运行更加安全稳定(尤其是使用了errno和fputs等方法后),在编译时最好进行_REENTRANT宏定义,使其变为可重入例程。具体方法为:$ gcc -D_REENTRANT f1.c -o f1.out -lpthread3) 调试器gdb的使用gdb是一个很强大的调试工具,熟练的使用可以让调试程序事半功倍。具体的内容参看ppt:“Linux常用开发工具”。4) Linux下的C编程常用头文件介绍stdio.h标准I/O库unistd.h符号常数string.h字符串操作pthread.h线程头文件(编写多线程必需)关于其中的函数将在下面做详细介绍。semaphore.h信号量头文件(编写有信号量的多线程时使用)关于其中的函数将在下面做详细介绍。3. Linux多线程编程相关函数介绍1) 主要线程函数介绍创建线程实际上就是确定调用该线程函数的入口点,这里通常使用的函数是pthread_create。在线程创建以后,就开始运行相关的线程函数,在该函 数运行完之后,该线程也就退出了,这也是线程退出一种方法。另一种退出线程的方法是使用函数pthread_exit,这是线程的主动行为。在线程中使用 pthread_exit来代替进程中的exit。 由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以用 wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。pthread_join可以用于将当 前线程挂起,等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。下面将对pthread_create与pthread_join函数分别介绍:a) 线程创建函数pthread_create()所需头文件 #include 函数原型 int pthread_create (pthread_t *thread, pthread_attr_t *attr, void *(*thread_function)(void *), void *arg) 函数形参:pthread_t *thread: 线程标识符 第一个参数是指向pthread_t类型数据的指针。线程被创建时,这个指针指向的变量中将被写入一个标识符,我们用该标识符来引用新线程。pthread_attr_t *attr: 线程属性设置我们一般不需要特殊的属性,所以可以简单地设置该参数为NULL。void *(*thread_function)(void *):线程函数的起始地址 第三个参数告诉我们必须要传递一个函数的地址,该函数以一个指向void的指针为参数,返回的也是一个指向void型的指针。因此可以传递一个任意类型的参数并返回任意类型的指针(只用强制转换即可)。新线程将在以这个函数为入口开始执行。void *arg: 传递给thread_function 的参数 第四个参数指明了thread_function函数的void *型参数。 函数返回值 成功:0 出错:错误代码b) 线程终止函数pthread_exit()所需头文件 #include 函数原型 void pthread_exit(void *retval)功能:由线程执行体调用将自身终止。retval定义了线程的返回值。c) 控制线程同步的函数pthread_join()所需头文件 #include 函数原型 int pthread_join (pthread_t th, void *thread_return) 函数传入值 th:指定了将要等待的线程,线程通过pthread_create返回的标识符来指定。 thread_return:是一个指针,它指向另一个指针,而后者指向线程的返回值。 函数返回值 成功:0 出错:错误代码*以上函数的具体应用参见下面的例程*2) 信号量信号量也就是操作系统中所用到的PV原语,它广泛用于进程或线程间的同步与互斥。信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。 PV原语是对整数计数器信号量sem的操作。一次P操作使sem减一,而一次V操作使sem加一。进程(或线程)根据信号量的值来判断是否对公共资源具有 访问权限。当信号量sem的值大于等于零时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem的值小于零时,该进程(或线程)就将阻塞直到 信号量sem的值大于等于0为止。以下函数的头文件均为:semaphore.ha) sem_init()功能:用于创建一个信号量,并能初始化它的值。所需头文件 #include 函数原型 int sem_init(sem_t *sem,int pshared,unsigned int value) 函数形参:sem_t *sem:这个函数初始化由sem指向的信号量对象。 pshared:决定信号量能否在几个进程间共享。由于目前Linux 还没有实现进程间共享信号量,所以这个值只能够取0value :信号量初始化值 函数返回值 成功:0 出错:错误码b) sem_wait()和sem_trywait()功能:相当于P操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait将会阻塞进程,而sem_trywait则会立即返回。 函数原型:int sem_wait(sem_t *sem) , int sem_trywait(sem_t *sem)函数形参:sem_t *sem:指向被操作信号量对象的指针c) sem_post()功能:相当于V操作,它将信号量的值加一同时发出信号唤醒等待的进程。函数原型:int sem_post(sem_t *sem)函数形参:sem_t *sem:指向被操作信号量对象的指针。d) sem_getvalue()功能:用于得到信号量的值。函数原型:int sem_ getvalue (sem_t *sem)函数形参:sem_t *sem:指向被操作信号量对象的指针。e) sem_destroy()功能:用于删除信号量。函数原型:int sem_destroy (sem_t *sem)函数形参:sem_t *sem:指向被操作信号量对象的指针。3) 互斥锁mutex是一种简单的加锁的方法来控制对共享资源的存取。这个互斥锁只有两种状态,也就是上锁和解锁,可以把互斥锁看作某种意义上的全局变量。在同一时 刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经上锁了的互斥锁,则该线程就会挂起,直到上 锁的线程释放掉互斥锁为止。可以说,这把互斥锁使得共享资源按序在各个线程中操作。a) 互斥锁初始化:pthread_mutex_init()函数原型: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 函数形参: pthread_mutex_t *mutex:与信号量类似,这些函数的参数都是一个先声明过的对象或指针,对互斥量来说,这个对象类型为pthread_mutex_t。const pthread_mutexattr_t *mutexattr:这个函数允许我们设置互斥锁的属性,如果设置为NULL,属性默认类型为快速类型。类型详见pthread_mutex_init使用手册。b) 互斥锁上锁:pthread_mutex_lock()函数原型:int pthread_mutex_lock(pthread_mutex_t *mutex)函数形参:与信号量类似,不再详述。c) 互斥锁解锁:pthread_mutex_unlock()函数原型:int pthread_mutex_unlock(pthread_mutex_t *mutex)d) 撤销互斥锁:pthread_mutex_destroy()函数原型:int pthread_mutex_ destroy(pthread_mutex_t *mutex)4. Linux下的多线程编程引例*下面针对Linux多线程编程的知识点由浅入深的给出若干引例*1) 引例一:基本的线程程序理解Linux多线程程序的基本结构a) 源代码#include #include #include #include void *thread_function(void *arg);char message = Hello World;int main()int res;pthread_t a_thread;void *thread_result;res = pthread_create(&a_thread,NULL,thread_function,(void *)message);if(res != 0)perror(Thread creation failed);exit(EXIT_FAILURE);printf(Waiting for thread to finish.n);res = pthread_join(a_thread,&thread_result);if(res != 0)perror(Thread join failed);exit(EXIT_FAILURE);printf(Thread joined,it returned %sn,(char *)thread_result);printf(Message is now %sn,message);exit(EXIT_SUCCESS);void *thread_function(void *arg)int print_count2 = 0; printf(thread_function is running. Argument was %sn,(char *)arg);sleep(3);strcpy(message,Bye!);pthread_exit(Thank you for the CPU time);b) 程序解析首先,我们定义了在创建线程时需要由它调用的一个函数的原型。如下所示:void *thread_function(void *arg)根据pthread_create的要求,它只有一个指向void的指针作为参数,返回的也是指向void的指针。稍后,我们将介绍这个函数的定义(及其实现)。在main函数中,我们首先定义了几个变量,然后调用pthread_create开始运行新线程。如下所示:pthread_t a_thread;void *thread_result;res = pthread_create(&a_thread,NULL,thread_function,(void *)message);我们向pthread_create函数传递了一个pthread_t类型对象的地址,今后可以用它来引用这个新线程。我们不想改变默认的线程的属性,所以设置第二个参数为NULL。最后两个参数分别为将要调用的函数和一个传递给该函数的参数。如果这个调用成功了,就会有两个线程在运行。原先的线程(main)继续执行pthread_create后面的代码,而新线程开始执行thread_function函数。原先的线程在查明新线程已经启动后,将调用pthread_join函数,如下所示:res = pthread_join(a_thread,&thread_result);我们给该函数传递两个参数,一个是正在等待其结束的线程的标识符,另一个是指向线程返回值的指针。这个函数将等到它所指定的线程终止后才返回。然后主线程将打印新线程的返回值和全局变量message的值,最后退出。新线程在thread_function函数中开始执行,它先打印出自己的参数,休眠一会儿,然后更新全局变量,最后退出并向主线程返回一个字符串。新线程修改了数组message,而原先的线程也可以访问该数组。如果我们调用的是fork而不是pthread_create,就不会有这样的效果。2) 引例二:两个线程的同时执行最简单的线程协调模型a) 源代码#include #include #include #include void *thread_function(void *arg);int run_now = 1;int main()int res;int print_count1 = 0;pthread_t a_thread;void *thread_result;res = pthread_create(&a_thread,NULL,thread_function,NULL);if(res != 0)perror(Thread creation failed);exit(EXIT_FAILURE);while(print_count1+ 20)if(run_now = 1)printf(1);run_now = 2;elsesleep(1);printf(nWaiting for thread to finish.n);res = pthread_join(a_thread,&thread_result);if(res != 0)perror(Thread join failed);exit(EXIT_FAILURE);printf(Thread joinedn);exit(EXIT_SUCCESS);void *thread_function(void *arg)int print_count2 = 0; while(print_count2+ 20)if(run_now = 2)printf(2);run_now = 1;elsesleep(1);sleep(3);b) 程序解析每个线程通过设置run_now变量的方法来通知另一个线程开始运行,然后,它会等待另一个线程改变了这个变量的值后再次运行。这个例子显示了两个线程之间自动地交替执行,同时也再次阐明了一个观点,即这两个线程共享run_now变量。3) 引例三:信号量的使用借助信号量体现多线程分工合作的思想a) 源代码#include #include #include #include #include #include void *thread_function(void *arg);sem_t bin_sem;#define WORK_SIZE 1024char work_areaWORK_SIZE;int main()int res;pthread_t a_thread;void *thread_result;res = sem_init(&bin_sem,0,0);if(res != 0)perror(Semaphore initialization failed);exit(EXIT_FAILURE);res = pthread_create(&a_thread,NULL,thread_function,NULL);if(res != 0)perror(Thread creation failed);exit(EXIT_FAILURE);printf(Input some text.Enter end to finishn);while(strncmp(end,work_area,3) != 0)fgets(work_area,WORK_SIZE,stdin);sem_post(&bin_sem);printf(nWaiting for thread to finish.n);res = pthread_join(a_thread,&thread_result);if(res != 0)perror(Thread join failed);exit(EXIT_FAILURE);printf(Thread joinedn);sem_destroy(&bin_sem);exit(EXIT_SUCCESS);void *thread_function(void *arg)sem_wait(&bin_sem);while(strncmp(end,work_area,3) != 0)printf(You input %d charactersn,strlen(work_area) - 1);sem_wait(&bin_sem);pthread_exit(NULL);b) 程序解析初始化信号量时,我们把它的值设置为0。这样,在线程函数启动时,sem_wait函数调用就会阻塞并等待信号量变为非零值。在主线程中,我们等待直到有文本输入,然后调用sem_post增加信号量的值,这将立刻令另一个线程从sem_wait的等待中返回并开始执行。在统计完字符个数之后,它再次调用sem_wait并再次被阻塞,直到主线程再次调用sem_post增加信号量的值为止。4) 引例四:使用互斥量将引例三改为用互斥量实现a) 源代码#include #include #include #include #include #include void *thread_function(void *arg);pthread_mutex_t work_mutex;/*protects both work_area and time_to_exit*/#define WORK_SIZE 1024char work_areaWORK_SIZE;int time_to_exit = 0;int main()int res;pthread_t a_thread;void *thread_result;res = pthread_mutex_init(&work_mutex,NULL);/fast modeif(res != 0)perror(Mutex initialization failed);exit(EXIT_FAILURE);res = pthread_create(&a_thread,NULL,thread_function,NULL);if(res != 0)perror(Thread creation failed);exit(EXIT_FAILURE);pthread_mutex_lock(&work_mutex);printf(Input some text.Enter end to finishn);while(!time_to_exit)fgets(work_area,WORK_SIZE,stdin);pthread_mutex_unlock(&work_mutex);while(1)pthread_mutex_lock(&work_mutex);if(work_area0 != 0)pthread_mutex_unlock(&work_mutex);sleep(1);else break;pthread_mutex_unlock(&work_mutex);printf(nWaiting for thread to finish.n);res = pthread_join(a_thread,&thread_result);if(res != 0)perror(Thread join failed);exit(EXIT_FAILURE);printf(Thread joinedn);pthread_mutex_destroy(&work_mutex);exit(EXIT_SUCCESS);void *thread_function(void *arg)sleep(1);pthread_mutex_lock(&work_mutex);while(strncmp(end,work_area,3) != 0)printf(You input %d charactersn,strlen(work_area) - 1);work_area0 = 0;pthread_mutex_unlock(&work_mutex);sleep(1);pthread_mutex_lock(&work_mutex);time_to_exit = 1;work_area0 = 0;pthread_mutex_unlock(&work_mutex);pthread_exit(0);b) 程序解析这段代码的完成的功能和上例基本相同,因此不再过多解析。值得提出的是,这段代码中通过轮询的方法获得结果通常并不是好的编程方式,在实际的编程中

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论