首页 > 编程知识 正文

Linux多线程命令,linux多线程下载

时间:2023-05-05 10:44:31 阅读:119466 作者:878

Linux多线程编程1

发布时间: 2019-03-21 20:10、

浏览次数: 454

、标签:

Linux

1 .多线程理解

在操作系统原理术语中,线程是进程的执行路径。 线程在Unix系统中通常称为轻量级进程

、线程不是进程,但可以视为Unix进程的表兄弟,所有线程都在同一进程空间中

中选择所需的墙类型。 这意味着多个线程共享进程中的所有系统资源,包括虚拟地址空间、文件描述符和信号处理。 但是,同一进程中的多个线程必须包含各自的调用栈(

堆栈)、自己的寄存器环境(注册上下文)、自己的线程本地存储(热本地存储)。

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

线程允许APP应用程序在多核环境中改进文件I/O和套接字等处理

发生I/O等堵塞时的性能。 在Unix系统中,一个进程包含许多资源,如可执行文件和文件描述符地址空间。 在大多数情况下,完成相关任务

需要在不同的代码之间交换数据。 对于多进程方法,创建进程所需的时间片

大于线程,且进程之间通信麻烦,需要频繁切换用户空间和内核空间,开销大。 但是,使用多线程方法,可以使用共享的全局变量,因此线程之间的通信(数据交换)非常高效。

多进程和多线程的区别

多线程:主线程是你父母的房间,子线程是你的房间。 你们要吃饼干,在客厅里拿就行了。

多进程:你父母的房间在一楼,你的房间在另一楼。 两个房间的通信用IPC协议。 我们使用的插座是这个协议。

所以多线程比多进程更方便。 因为你们在同一所房子里。 )

2 .创建线程

创建进程时,首先生成默认线程。 此线程通常称为主线程或控制线程。 在C/C程序中,主线程通过main函数输入,并由主线程调用

用pthread_create ()创建的线程称为子线程,子线程也可以具有用户在创建时指定的自己的条目函数。 每个线程都有自己的线程ID,可以通过

pthread_self ()

获取函数。 在最常见的线程模型中,除了主线程是特殊的以外,当创建其他线程时,它们是相互对等的关系,不存在隐式层次关系。 每个进程可以创建的最大线程数取决于实现。

无论是windows还是Posix,主线程和子线程的默认关系是,无论子线程是否已完成运行,当主线程运行完成并退出时,所有子线程的运行都将终止。 此时,整个进程已终止或僵硬,一些线程已结束运行但尚未销毁,进程必须在所有线程销毁后销毁,此时进程已僵硬。 线程函数执行完成并退出,或在其他紧急情况下退出,线程进入退出状态。 但是,分配给线程的系统资源不一定释放,并且在系统重新启动之前可能不会释放。 已终止线程仍然作为线程的实体存在于操作系统中,并且何时被销毁取决于线程的属性。 在这种情况下,主线程和子线程通常定义以下两种关系:

*可以合流

(joinable )在此关系中,主线程必须显式执行等待操作,子线程完成等待操作,子线程与主线程合并,此时主线程执行等待操作之后的下一个操作。 主线程必须与可会晤的子线程合并。 在主线程线程函数内部调用子线程对象的等待函数的实现,即使在主线程之前已经执行了子线程且处于结束状态,也必须执行会议操作。 否则,系统将永远不会主动销毁线程或释放分配给该线程的系统资源。

*相分离(detached )表示子线程不需要与主线程合并,也就是说相分离,在这种情况下,一旦子线程处于结束状态,线程数较多时常用

中选择所需的墙类型。 在并发子线程很多的情况下也常用,因为很难或不可能确定主线程每次一个子线程结束,或者主线程每次一个子线程结束的顺序。

线程的分离状态决定了一个线程如何退出自己。 默认情况下,线程处于非隔离状态。 在这种情况下,原始线程正在等待创建的线程结束。 只有时候

pthread_join

函数返回时,创建的线程将终止,并释放自己占用的系统资源。 另一方面,如果线程完成自己的运行,而不等待其他线程,则线程也会终止,并立即释放系统资源。

3. pthread_create ()函数

第36和39行调用pthread_create ()函数创建两个子线程。 此函数的原型如下:

# includeintpthread _ create (pthread _ t * thread,const

pthread_attr_t *attr、void*(start_routine ) void * )、void*arg ); 完成与

link with -pthrea

说明: pthreand_create ()用于创建线程并运行第三个参数start_routi

ne所指向的函数。

* 第三个参数start_routine是一个函数指针,它指向的函数原型是 void func(void),这是所创建的子线程要执行的任务 (函数);

* 第四个参数arg就是传给了所调用的函数的参数,如果有多个参数需要传递给子线程则需要封装到一个结构体里传进去;

* 第一个参数thread

是一个pthread_t类型的指针,他用来返回该线程的线程ID。每个线程都能够通过**pthread_self()**来获取自己的线程ID(pthread_t类型)

* 第二个参数是线程的属性,其类型是pthread_attr_t类型,其定义如下:

需要设定的是线程的分离状态,如果有必要也需要修改每个线程的栈大小。每个线程创建后默认是joinable状态,该状态需要主线程调用 pthread_join

等待它退出,否则子线程在结束时,内存资源不能得到释放造成内存泄漏。所以我们创建线程时一般会将线程设置为分离状态,具体有两种方法:

* 线程里面调用 pthread_detach(pthread_self()) 这个方法最简单

* 在创建线程的属性设置里设置PTHREADCREATEDETACHED

代码15行我们定义了创建线程的属性变量thread_attr ,在对该属性进行设置前,我们需要先调用pthread_attr_init 函数初

始化它(第18行),在第24行我们设置线程的栈大小为120K,同时在第30行设置线程的属性为分离状态。第36行创建线程时使用了该属性创建线程,这时创建的子进程就是分离状态了。线程属性在使用完之后,我们应该调用

pthread_attr_destroy (第42行)把他摧毁释放。

而代码39行创建子线程时并没有使用该线程,同时在thread_worker2() 里并没有调用pthread_detach()设置线程为分离状

态。这时就需要主线程在45行处调用pthread_join() 等待第二个子线程退出。当然主线程也就阻塞在这里不会往下继续执行

了。

在创建两个线程时,我们都通过第四个参数将主线程 栈中的 shared_var 变量地址传给了子线程,因为所有线程都是在同一进

程空间中运行,而只是子线程有自己独立的栈空间,所以这时所有子线程都可以访问主线程空间的shared_var变量。

现在我们编译运行一下程序看看效果,注意对于多线程编程在编译时,一定要加上-lpthread 选项告诉链接器在链接的时候要连

接pthread库:

gcc thread.c -o thread -lpthread ./thread

主线程创建子线程后究竟是子线程还是主线程先执行,或究竟哪个子线程先运行系统并没有规定,这个依赖操作系统的进程

调度策略。当然因为代码45行处主线程调用了pthread_join

会导致主线程阻塞,所以主线程不会往下继续执行while(1)循环。我们再来深入分析各个线程的代码:

我们在创建子线程之后,在子线程的执行函数里一般都会用while(1)的死循环来让子线程一直运行,否则子线程将按代码顺序

执行,执行完毕就线程退出了。同样的,我们主线程也应该要用一个while(1)循环一直运行,否则主线程退出会导致进程退出,而进程退出会导致所有子线程退出了。

接下来我们在分析一下子线程所做的事,在两个子线程中做的任务是首先打印一下当前 shared_var 变量的值然后让它自加,

之后睡眠2s后再打印一下这两个值,代码如下:

从上面的运行结果红色标注的部分我们可以看到,thread_worker1 在创建后首先开始运行,在开始自加之前值为初始值

1000,然后让该值自加后休眠2秒后再打印该值发现不是1001而是1002了。这是由于shared_var 这个变量会被两个子线程同时访问

修改导致。如果一个资源会被不同的线程访问修改,那么我们把这个资源叫做临界资源,那么对于该资源访问修改相关的代码就叫做临界区。那么怎么解决多个线程之间共享同一个共享资源,是多线程编程需要考虑的一个问题。

<>4. 互斥锁

试想一下,我们寝室/实验室只有一个洗手间,那多个人是怎么解决马桶共享的问题?对,那就是锁

的机制!在这里马桶就是临界资源,我们在进入到洗手间(临界区)后,就首先上锁;

然后用完离开洗手间(临界区)之后,把锁释放供别人使用。如果有人想去洗手间时发现门锁上了,他也有两种策略:1,在洗手间那里等(阻塞);

2,暂时先离开等会再过来看(非阻塞);

互斥锁 锁的是一个临界区,在临界区开头(加锁)lock,在临界区结尾(关锁)unlock。

<>死锁

如果多个线程要调用多个对象,则在上锁的时候可能会出现“死锁”。举个例子: A、B两个线程会同时使用到两个共享变量m和n,同时每个变量都有自己相应的锁M和N。

这时A线程首先拿到M锁访问m,接下来他需要拿N锁来访问变量n; 而如果此时B线程拿着N锁等待着M锁的话,就造成了线程“死锁”。

死锁产生的4个必要条件:

* 互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。

* 占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。

* 不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。

* 循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。 当

以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU

的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能

的。那么,解决死锁问题就是相当有必要的了。产生死锁需要四个条件,那么,只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于

互斥条件是非共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件。

<>a、破坏“占有且等待”条件

方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源。

优点:简单易实施且安全。

缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成

资源浪费。使进程经常发生饥饿现象。

方法2

:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。

<>b、破坏“不可抢占”条件

当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源

,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作实效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。

<>c、破坏“循环等待”条件

可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程占有编号为i的资源时,那么它下一次申请资源只

能申请编号大于i的资源。

<>5. 多线程改写服务器程序

多线程编程模型和多进程编程模型的工作方式完全一致,在了解Linux下多进程编程之后,我们就可以使用多进程编程模型改写

服务器的多线程实现,其流程图和程序代码如下:

在多进程基础上写多线程

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。