Linux进程与线程的编程(C/C++版)

Linux进程与线程的编程(C/C++版)


Linux的线程基础知识请跳转到 Linux命令行学习第12小节了解相关基础。

C/C++编译程序的实现请了解相关gcc和g++的命令行操作。

通过C/C++语言实现多线程操作,主要过程是了解进程和线程的状态,和C/C++的相关函数。

为了更进一步的使用C/C++编程多线程程序,希望大家先了解其中有关进程/线程操作的函数:

fork()函数

作用:创建一个新进程

创建过程:一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

创建结果:在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

例如我们创建下列.cpp文件:

则其编译生成后的可运行文件在终端运行后的结果如下:

通过上面的实例,可以看出fork()的作用。

vfork()函数

不同于vfork()函数,其创建的子进程共父进程的空间,创建子进程后,父进程会被阻塞直到执行exec()或exit()。

即其子进程会和父进程使用的是同样的内存资源,子进程对父进程的改变同样会影响父进程

如下列.cpp文件的运行文件的结果:

注意vfork必须有exit()或exec()跳出,不然由于return则父程序会被直接破坏掉,从而产生灾难性的错误。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mygYapIe-1618187971926)(C:\Users\wizard\AppData\Roaming\Typora\typora-user-images\1618185690922.png)]

clone()函数

其作用是创建线程,但后面介绍的pthread_create()函数有类似的作用,故在这里不具体展开。

exec()函数

作用:使进程引入新程序

我们将后缀l,v,p,e添加在exec()函数后,可使其具有一定的功能

p:可直接执行PATH变量下的文件(/bin/*),无需在加地址

l:可以接收以逗号为分隔的参数列表,以NULL指针作为结束标注

v:可以接收以逗号为分隔的字符串,以NULL指针作为结束标注

e:允许改变子进程环境

进程等待

当我们使用fork()时,其创建的子进程的顺序和父进程的顺序时无法预测的,所以我们需要使用wait()和waitpid()函数来避免错误

其中的waitpid()用来指定特定的pid进程,可以使用getpid()来获取进程。

进程终止(销毁)

可以使用exit(0),前面的例子已经使用多次,不再赘述。

线程的创建和删除

线程包提供了一组函数用来创建,删除线程

线程创建:

可以使用clone()函数(创建内核支持的线程)和pthread_create()函数(创建内核不可见的线程,由线程库调度)创建线程。这里我们使用pthread_create()函数进行创建。

结果为:

Pthread_create is created...
休眠

sleep(time)//休眠time秒

例如上面的c文件,我们将其休眠

结果为:

Pthread_create is created...  #注意虽然结果相同,但是是五秒后打印的结果
线程同步

这里使用公共计数器和互斥锁来实现线程之间的同步

我们用简单的买票系统来实验互斥锁:

未加锁:

结果:

我们发现,虽然只有20张票,但最终已经卖了23张,这显然不正确。

而当我们使用互斥锁后:

问题得到了解决。

那么互斥锁如何使用呢?

我们通过Pthread_mutex_lock()和Pthread_mutex_unlock()进行锁和解锁。

在这里,我们将互斥锁Pthread_mutex_lock(ticket_sum)和Pthread_mutex_unlock(ticket_sum)放在在票卖出语句前后,从而实现在同一时间只有一个进程引用票数。

线程结束

exit();在前面的程序中都有所体现,这里不再赘述。

线程等待

pthread_join(线程,NULL)函数,让主线程等待子线程完成再结束。这个函数防止了子线程还没有运行完主线程就结束的危险。

学以致用,我们使用上述方法,设计三个与进程/线程有关的程序:

通过进程的创建,用子进程打印超级玛丽。

​ 思路:使用vfork()函数创建进程,从而实现先于父进程读取文件,读后再让父进程运行,但这样的话子父进程会调用同一资源而造成不必要的麻烦,故不考虑,或使用fork()和wait()实现使父进程等待子进程,此时调用的是两部分资源,可行。

​ 代码部分:

#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
using namespace std;
int main()
{
    int pid=fork();
    if(pid<0)
    printf("创建失败了");
    else
    {
    if(pid==0)
    {
  cout << "                ********" << endl;
    cout << "               ************" << endl;
    cout << "               ####....#." << endl;
    cout << "             #..###.....##...." << endl;
    cout << "             ###.......######              ###                 ###           ###           ###" << endl;
    cout << "                ...........               #...#               #...#         #...#         #...#" << endl;
    cout << "               ##*#######                 #.#.#               #.#.#         #.#.#         #.#.#" << endl;
    cout << "            ####*******######             #.#.#               #.#.#         #.#.#         #.#.#" << endl;
    cout << "           ...#***.****.*###....          #...#               #...#         #...#         #...#" << endl;
    cout << "           ....**********##.....           ###                 ###           ###           ###" << endl;
    cout << "           ....****    *****...." << endl;
    cout << "             ####        ####" << endl;
    cout << "           ######        ######" << endl;
    cout << "##############################################################              ##################################" << endl;
    cout << "#...#......#.##...#......#.##...#......#.##------------------#              #...#......#.##------------------#" << endl;
    cout << "###########################################------------------#              ###############------------------#" << endl;
    cout << "#..#....#....##..#....#....##..#....#....#####################              #..#....#....#####################" << endl;
    cout << "##########################################    #----------#                  ##############    #----------#" << endl;
    cout << "#.....#......##.....#......##.....#......#    #----------#                  #.....#......#    #----------#" << endl;
    cout << "##########################################    #----------#                  ##############    #----------#" << endl;
    cout << "#.#..#....#..##.#..#....#..##.#..#....#..#    #----------#                  #.#..#....#..#    #----------#" << endl;
    cout << "##########################################    ############                  ##############    ############" << endl;
    }
    else
    {
    wait(NULL);//等待子进程 byAEI
    cout<<"子进程打印了超级玛丽"<<endl;
    }
    }
    return 0;
}

结果:

首先子进程打印出超级玛丽:

后出现父进程结果:

子进程打印了超级玛丽 #虽然紧接着超级玛丽的打印而打印出来,但要知道是两个进程
线程共享进程数据

思路:使用线程创建函数创建10个线程,并定义共享数据来共享。

#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
using namespace std;
string s="create thread";
void *create(void *arg)
{
    cout<<s<<endl;
    return 0;
}
int main(void)
{
    pthread_t tidp[10];
    for(int i=0;i<10;i++)
    {
    int error;
    error=pthread_create(&tidp[i],NULL,create,NULL);
    if(error !=0)
    {
        cout<<"thread"<<i<<"create erroro!\n";
        return -1;   
    }
    }
    return 0;
}

实现结果:

为什么会出现有的线程输出在同一行,有的却单空出一行呢?因为线程在不上锁的情况下,会出现抢占资源的情况,从而导致可能一个线程的执行并不是先后执行的,而是谁抢到了CPU谁继续执行。

那么如何解决这样的问题呢?我们可以在每个线程创建后,执行sleep()函数,先让主进程睡一会,在这期间,新创建的线程已经执行完,从而近似的解决了线程冲突。

但这样的操作会让程序执行时间大大增加,所以我们可以考虑另一种方案,即互斥锁的操作。

多线程实现单词统计(利用互斥锁)

思路:为了实现多线程统计单词,我们不仅需要多线程之间引用公共资源,还需要为线程上锁,同时为了不让主线程在子线程前提前结束,还需要使用线程等待函数让主线程等待子线程完结。

代码:

结果:

虽然两个线程执行顺序可能不同,每一个线程也也可能和另一个线程交替运行,但由于添加了互斥锁,共享资源单词数的计算不会受影响。

同时若我们忽略其中一个线程,则会产生只统计其中一个文件单词数的现象:

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wizardAEI

喜欢就投喂一下吧O(∩_∩)O

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值