Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

合集下载

关于Linux多线程编程

关于Linux多线程编程

关于Linux多线程编程Linux线程分为两类,一是核心级支持线程,在核心级实现线程时,线程的实现依赖于内核,无论是在用户进程中的线程还是系统进程中的线程,他们的创建、撤消、切换都由内核实现。

核心只有单线程进程概念,而多线程进程由与应用程序连接的过程库实现。

另一类线程是用户级线程,在Linux众多的线程库中,大部分实现的是用户级线程。

系统创建线程的顺序如下:当一个线程启动后,它会自动创建一个线程即主线程(main thread)或者初始化线程(initial thread),然后就利用pthread_initialize()初始化系统管理线程并且启动线程机制。

Linux线程编程基础要创建一个多线程程序,必须加载pthread.h头文件。

要掌握多线程编程常用的几个函数:1、创建新线程函数:pthread_create()2、挂起当前线程函数:pthread_join()3、线程注册的清除处理函数:pthread_exit()4、取消一个线程函数:pthread_cancel()5、挂起当前线程,直到满足某种条件:pthread_cond_init多线程的同步1、互斥锁互斥锁用来保证一段时间内只有一个线程在执行一段代码。

当在同一内存空间运行多个线程时,为保证多个线程之间不相互破坏,要创建互斥量,如果一个线程已经锁定一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程被挂起(不占用任何CPU资源),直到第一个线程解除对这个互斥量的锁定为止。

第二个线程将被唤醒并继续执行,同时锁定这个互斥量。

创建互斥量时,必须首先声明一个类型为pthread_mutex_t的变量,然后对其进行初始化,结构pthread_mutex_t为不公开的数据类型,其中包含一个系统分配的属性对象。

函数pthread_mutex_init用来生成一个互斥锁。

锁定一个互斥量时使用函数pthread_mutex_lock(),它尝试锁定一个互斥量,如果该互斥量已经被其它线程锁定,该函数就把调用自己的线程挂起,一旦该互斥量解锁,它将恢复运行并锁定该互斥量。

linux多线程编程详解教程(线程通过信号量实现通信代码)

linux多线程编程详解教程(线程通过信号量实现通信代码)

linux多线程编程详解教程(线程通过信号量实现通信代码)线程按照其调度者可以分为⽤户级线程和核⼼级线程两种。

(1)⽤户级线程主要解决的是上下⽂切换的问题,它的调度算法和调度过程全部由⽤户⾃⾏选择决定,在运⾏时不需要特定的内核⽀持。

在这⾥,操作系统往往会提供⼀个⽤户空间的线程库,该线程库提供了线程的创建、调度、撤销等功能,⽽内核仍然仅对进程进⾏管理。

如果⼀个进程中的某⼀个线程调⽤了⼀个阻塞的系统调⽤,那么该进程包括该进程中的其他所有线程也同时被阻塞。

这种⽤户级线程的主要缺点是在⼀个进程中的多个线程的调度中⽆法发挥多处理器的优势。

(2)这种线程允许不同进程中的线程按照同⼀相对优先调度⽅法进⾏调度,这样就可以发挥多处理器的并发优势。

现在⼤多数系统都采⽤⽤户级线程与核⼼级线程并存的⽅法。

⼀个⽤户级线程可以对应⼀个或⼏个核⼼级线程,也就是“⼀对⼀”或“多对⼀”模型。

这样既可满⾜多处理机系统的需要,也可以最⼤限度地减少调度开销。

Linux的线程实现是在核外进⾏的,核内提供的是创建进程的接⼝do_fork()。

内核提供了两个系统调⽤clone()和fork(),最终都⽤不同的参数调⽤do_fork()核内API。

当然,要想实现线程,没有核⼼对多进程(其实是轻量级进程)共享数据段的⽀持是不⾏的,因此,do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享⽂件系统信息)、CLONE_FILES(共享⽂件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。

当使⽤fork系统调⽤时,内核调⽤do_fork()不使⽤任何共享属性,进程拥有独⽴的运⾏环境,⽽使⽤pthread_create()来创建线程时,则最终设置了所有这些属性来调⽤__clone(),⽽这些参数⼜全部传给核内的do_fork(),从⽽创建的“进程”拥有共享的运⾏环境,只有栈是独⽴的,由__clone()传⼊。

05Linux多线程编程 多核编程

05Linux多线程编程 多核编程

线程显示地退出外,线程也可以使用 pthread_cancel() 函数 终止其他线程的执行。
int pthread_cancel(pthread_t thread);
等待线程结束
• pthread_join() 函数会挂起创建线程的线程的执行,直到等
待到想要等待的子线程。
int pthread_join(pthread_t th, void **thread_return);
线程的退出
• 在线程的处理函数中,可以显示的调用行,也可以不调用pthread_exit(),而只是让线程处 理程序返回。
void pthread_exit (void* retval);
• 除了pthread_exit() 函数,可以让当前调用pthread_exit() 的
代价昂贵,通常子进程需要拷贝父进程的整个上下文,比如数据等。 进程间的通信方式比较复杂,比如使用管道、消息、共享内存等方法。 操作系统在实现进程间的切换比线程切换更费时。
• 使用POSIX pthreads库创建线程的特点:
线程可使用存在于进程中的资源。 线程间的通信方式更容易,比如通过进程中的变量,可以让多个线程共享数据。 操作系统对线程的切换比对进程的切换更容易和快速。
Linux多线程编程 Linux多线程编程
IEEE POSIX 标准 p1003.1c (Pthreads) 定义了处理线程的一系 列C 语言类型的API。 在Linux中,线程一般被认为是“轻量级的进程”。 Linux 创建进程所使用的函数是fork() 或者vfork()。而对线程 的创建和管理Linux 可以使用POSIX的线程库pthreads提供的 APIs。 使用fork()创建进程和使用POSIX线程库差别: • 使用fork() 创建进程的特点:

Linux下的多线程编程上机学习材料

Linux下的多线程编程上机学习材料

Linux下的多线程编程上机准备吴雨龙目录1.我们为什么要练习多线程?(多线程的好处) (3)2.Linux下的多线程编程必备知识 (3)1)VIM的使用 (3)a)指令模式下常用命令 (3)b)插入模式 (4)2)编译器gcc的使用必知 (4)3)调试器gdb的使用 (4)4)Linux下的C编程常用头文件介绍 (4)3.Linux多线程编程相关函数介绍 (5)1)主要线程函数介绍 (5)a)线程创建函数pthread_create() (5)b)线程终止函数pthread_exit() (6)c)控制线程同步的函数pthread_exit() (6)2)信号量 (7)a)sem_init() (7)b)sem_wait()和sem_trywait() (7)c)sem_post() (8)d)sem_getvalue() (8)e)sem_destroy() (8)3)互斥锁 (8)a)互斥锁初始化:pthread_mutex_init() (8)b)互斥锁上锁:pthread_mutex_lock() (9)c)互斥锁解锁:pthread_mutex_unlock() (9)d)撤销互斥锁:pthread_mutex_destroy() (9)4.Linux下的多线程编程引例 (9)1)引例一:基本的线程程序——理解Linux多线程程序的基本结构 (9)a)源代码 (9)b)程序解析 (10)2)引例二:两个线程的同时执行——最简单的线程协调模型 (11)a)源代码 (11)b)程序解析 (12)3)引例三:信号量的使用——借助信号量体现多线程分工合作的思想 (12)a)源代码 (12)b)程序解析 (14)4)引例四:使用互斥量——将引例三改为用互斥量实现 (14)a)源代码 (14)b)程序解析 (16)5)引例五:一个简单的真正多个线程的程序——真正的多线程 (16)a)源代码 (16)b)程序解析 (17)c)思考一下吧... .. (18)1.我们为什么要练习多线程?(多线程的好处)1) 提高应用程序响应。

多线程编程之:Linux线程编程

多线程编程之:Linux线程编程

多线程编程之:Linux线程编程9.2 线程编程9.2.1 线程基本编程这里要讲的线程相关操作都是用户空间中的线程的操作。

在Linux中,普通pthread线程库是一套通用的线程库,是由POSIX提出的,因此具有很好的可移植性。

(1)函数解释。

创建线程事实上就是确定调用该线程函数的入口点,这里通常用法的函数是pthread_create()。

在线程创建以后,就开头运行相关的线程函数,在该函数运行完之后,该线程也就退出了,这也是线程退出一种办法。

另一种退出线程的办法是用法函数pthread_exit(),这是线程的主动行为。

这里要注重的是,在用法线程函数时,不能任意用法exit()退出函数举行出错处理,因为exit()的作用是使调用进程终止,往往一个进程包含多个线程,因此,在用法exit()之后,该进程中的全部线程都终止了。

因此,在线程中就可以用法pthread_exit()来代替进程中的exit()。

因为一个进程中的多个线程是分享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。

正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。

pthread_join()可以用于将当前线程挂起来等待线程的结束。

这个函数是一个线程堵塞的函数,调用它的函数将向来等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。

前面已提到线程调用pthread_exit()函数主动终止自身线程。

但是在无数线程应用中,常常会碰到在别的线程中要终止另一个线程的执行的问题。

此时调用pthread_cancel()函数实现这种功能,但在被取消的线程的内部需要调用pthread_setcancel()函数和pthread_setcanceltype()函数设置自己的取消状态,例如被取消的线第1页共9页。

C语言多线程编程入门指南

C语言多线程编程入门指南

C语言多线程编程入门指南C语言是一种强大的编程语言,它提供了多线程编程的支持,使程序能够同时执行多个任务。

多线程编程在现代应用程序开发中非常重要,可以提高程序的性能和响应能力。

本文将为您介绍C语言多线程编程的基本概念和使用方法,帮助您入门多线程编程。

一、多线程编程概述多线程是一种并发编程的方法,可以使程序同时执行多个任务。

在传统的单线程编程中,程序按照先后顺序顺序执行每个任务,而在多线程编程中,可以同时执行多个任务,提高程序的效率和响应能力。

二、C语言多线程库C语言提供了多线程编程的库,最常用的是POSIX线程库(pthread)。

POSIX线程库定义了多个函数,用于创建和管理线程,还提供了同步和互斥等机制,确保多个线程之间的正确协作。

三、线程的创建和终止1. 创建线程在C语言中创建线程需要使用pthread_create()函数。

以下是创建线程的基本语法:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);2. 终止线程线程运行结束后,可以使用pthread_exit()函数终止线程。

以下是终止线程的基本语法:void pthread_exit(void *value_ptr);四、线程同步在多线程编程中,为了避免线程之间的冲突和竞争条件,需要使用同步机制。

1. 互斥锁互斥锁是一种最常用的线程同步机制,可以保护临界区资源的访问。

使用互斥锁可以确保同一时刻只有一个线程可以进入临界区。

以下是互斥锁的基本使用方法:pthread_mutex_t mutex; // 定义互斥锁pthread_mutex_init(&mutex, NULL); // 初始化互斥锁// 在临界区操作之前加锁pthread_mutex_lock(&mutex);// 临界区操作// ...// 在临界区操作之后解锁pthread_mutex_unlock(&mutex);2. 条件变量条件变量用于线程之间的等待和通知机制。

Linux下c语言多线程编程

Linux下c语言多线程编程

Linux下c语⾔多线程编程引⾔ 线程(thread)技术早在60年代就被提出,但真正应⽤多线程到中去,是在80年代中期,solaris是这⽅⾯的佼佼者。

传统的Unix也⽀持线程的概念,但是在⼀个进程(process)中只允许有⼀个线程,这样多线程就意味着多进程。

现在,多 为什么有了进程的概念后,还要再引⼊线程呢?使⽤多线程到底有哪些好处?什么的系统应该选⽤多线程?我们⾸先必须回答这些问题。

使⽤多线程的理由之⼀是和进程相⽐,它是⼀种⾮常"节俭"的多任务操作⽅式。

我们知道,在Linux系统下,启动⼀个新的进程必须分配给它独⽴的地址空间,建⽴众多的数据表来维护它的代码段、堆栈段和数据段,这是⼀种"昂贵"的多任务⼯作⽅式。

⽽运⾏于⼀个进程中的多个线程,它们彼此之间使⽤相同的地址空间,共享⼤部分数据,启动⼀个线程所花费的空间远远⼩于启动⼀个进程所花费的空间,⽽且,线程间彼此切换所需的时间也远远⼩于进程间切换所需要的时间。

使⽤多线程的理由之⼆是线程间⽅便的机制。

对不同进程来说,它们具有独⽴的数据空间,要进⾏数据的传递只能通过通信的⽅式进⾏,这种⽅式不仅费时,⽽且很不⽅便。

线程则不然,由于同⼀进程下的线程之间共享数据空间,所以⼀个线程的数据可以直接为其它线程所⽤,这不仅快捷,⽽且⽅便。

当然,数据的共享也带来其他⼀些问题,有的变量不能同时被两个线程所修改,有的⼦程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地⽅。

除了以上所说的优点外,不和进程⽐较,多线程程序作为⼀种多任务、并发的⼯作⽅式,当然有以下的优点: 1) 提⾼应⽤程序响应。

这对图形界⾯的程序尤其有意义,当⼀个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应、、菜单的操作,⽽使⽤多线程技术,将耗时长的操作(time consuming)置于⼀个新的线程,可以避免这种尴尬的情况。

Linux多任务多线程编程-

Linux多任务多线程编程-
23
等待进程结束
当一个进程正常或异常终止时,内核就向其父进程发 送SIGCHLD信号。
Linux系统提供了waitid()函数,他们的作用是等待另外 一个进程的结束。函数定义如下: #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int *statloc, int options) ; 两个函数返回:若成功则为进程I D,若出错则为-1
3
(1)进程级多任务
在对话中可以并发激活多个进程,这些进程相 互合作来完成一个最终目标。
所有的进程共享CPU运行,一个进程运行一段 时间,然后另一个进程再运行一段时间。
操作系统控制进程之间的转换,直到所有的进
程运行完成。对于这样一种多个进程并发执行 的多任务实现方式,称作进程级多任务。
4
什么是进程?
if (-1==pid)
{
// 检查是否创建成功
printf("Error to create new process!\n");
return 0;
}
else if (pid==0)
{
// 子进程
printf("Child process!\n");
} else
{
// 父进程
printf("Parent process! Child process ID: %d\n", pid);
}
return 0;
}
20
fork出错的原因
系统中已经有了太多的进程
该实际用户I D的进程总数超过了系统限 制
CHILD_MAX规定了每个实际用户I D在任一 时刻可具有的最大进程
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

介绍:什么是线程,线程的优点是什么线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。

但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread­local storage)。

一个进程可以有很多线程,每条线程并行执行不同的任务。

线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。

在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。

在很多情况下,完成相关任务的不同代码间需要交换数据。

如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。

但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。

Hello World(线程创建、结束、等待)创建线程 pthread_create线程创建函数包含四个变量,分别为: 1. 一个线程变量名,被创建线程的标识 2. 线程的属性指针,缺省为NULL即可 3. 被创建线程的程序代码 4. 程序代码的参数 For example: ­ pthread_t thrd1; ­pthread_attr_t attr; ­ void thread_function(void argument); ­ char *some_argument;pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument);结束线程 pthread_exit线程结束调用实例:pthread_exit(void *retval); //retval用于存放线程结束的退出状态线程等待 pthread_joinpthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。

举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。

该函数包含两个参数:pthread_t th //th是要等待结束的线程的标识void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。

调用实例:pthread_join(thrd1, NULL);example1:1 /*************************************************************************2 > F i l e N a m e: t h r e a d_h e l l o_w o r l d.c3 > A u t h o r: c o u l d t t(f y b y)4 > M a i l: f u y u n b i y i@g m a i l.c o m5 > C r e a t e d T i m e: 2013年12月14日 星期六 11时48分50秒6 ************************************************************************/78 #i n c l u d e <s t d i o.h>9 #i n c l u d e <s t d l i b.h>10 #i n c l u d e <p t h r e a d.h>1112 v o i d p r i n t_m e s s a g e_f u n c t i o n (v o i d *p t r);1314 i n t m a i n()15 {16 i n t t m p1, t m p2;17 v o i d *r e t v a l;18 p t h r e a d_t t h r e a d1, t h r e a d2;19 c h a r *m e s s a g e1 = "t h r e a d1";20 c h a r *m e s s a g e2 = "t h r e a d2";2122 i n t r e t_t h r d1, r e t_t h r d2;2324 r e t_t h r d1 = p t h r e a d_c r e a t e(&t h r e a d1, N U L L, (v o i d *)&p r i n t_m e s s a g e_f u n c t i o n, (v o i d *) m e s s a g e1);25 r e t_t h r d2 = p t h r e a d_c r e a t e(&t h r e a d2, N U L L, (v o i d *)&p r i n t_m e s s a g e_f u n c t i o n, (v o i d *) m e s s a g e2);2627 // 线程创建成功,返回0,失败返回失败号28 i f (r e t_t h r d1 != 0) {29 p r i n t f("线程1创建失败\n");30 } e l s e {31 p r i n t f("线程1创建成功\n");32 }3334 i f (r e t_t h r d2 != 0) {35 p r i n t f("线程2创建失败\n");36 } e l s e {37 p r i n t f("线程2创建成功\n");38 }3940 //同样,p t h r e a d_j o i n的返回值成功为041 t m p1 = p t h r e a d_j o i n(t h r e a d1, &r e t v a l);42 p r i n t f("t h r e a d1 r e t u r n v a l u e(r e t v a l) i s %d\n", (i n t)r e t v a l);43 p r i n t f("t h r e a d1 r e t u r n v a l u e(t m p) i s %d\n", t m p1);44 i f (t m p1 != 0) {45 p r i n t f("c a n n o t j o i n w i t h t h r e a d1\n");46 }47 p r i n t f("t h r e a d1 e n d\n");4849 t m p2 = p t h r e a d_j o i n(t h r e a d1, &r e t v a l);50 p r i n t f("t h r e a d2 r e t u r n v a l u e(r e t v a l) i s %d\n", (i n t)r e t v a l);51 p r i n t f("t h r e a d2 r e t u r n v a l u e(t m p) i s %d\n", t m p1);52 i f (t m p2 != 0) {53 p r i n t f("c a n n o t j o i n w i t h t h r e a d2\n");54 }55 p r i n t f("t h r e a d2 e n d\n");5657 }5859 v o i d p r i n t_m e s s a g e_f u n c t i o n( v o i d *p t r ) {60 i n t i = 0;61 f o r (i; i<5; i++) {62 p r i n t f("%s:%d\n", (c h a r *)p t r, i);63 }64 }编译gcc thread_hello_world.c -otest -lpthread 一定要加上-lpthread,要不然会报错,因为源代码里引用了pthread.h里的东西,所以在gcc进行链接的时候,必须要找到这些库的二进制实现代码。

运行结果结果分析: 1.这段程序我运行了两次,可以看到,两次的运行结果是不一样的,从而说明,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度。

2.另外,我们看到,在thread2的join结果出现了错误,打印出cannot join with thread2其实这个是个小错误,因为,我pthread_join传进去的th是thread1,在上面的结果中,thread1早已经结束了,所以我们再次等待thread1结束肯定会出现无法取到状态的错误的。

3.pthread_join(thread1, &retval)确实等待了thread1的结束,我们看到,在print_message_function函数循环了5遍结束以后,才打印出thread1 end这是一个非常简单的例子,hello world级别的,只是用来演示Linux下C多线程的使用,在实际应用中,由于多个线程往往会访问共享的资源(典型的是访问同一个全局变量),因此多个县城间存在着竞争的关系,这就需要对多个线程进行同步,对其访问的数据予以保护。

多线程的同步与互斥方式一:锁在主线程中初始化锁为解锁状态pthread_mutex_t mutex;pthread_mutex_init(&mutex, NULL);在编译时初始化锁为解锁状态锁初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;访问对象时的加锁操作与解锁操作加锁 pthread_mutex_lock(&mutex)释放锁 pthread_mutex_unlock(&mutex)不加锁,数据不同步我们先来看一个不加锁,多个线程访问同一段数据的程序。

相关文档
最新文档