当前位置:   article > 正文

并发 多线程 多进程(c++)_c++多进程

c++多进程

基本概念

  • 并发:宏观上一段时间内有多个程序在同时运行,可以由操作系统来实现并发(进程/线程/协程)
  • 并行:同一时刻有多个指令同时运行,并行需要由硬件来支撑(多核处理器/分布式系统等)
  • 进程:资源分配和拥有的基本单位
  • 线程:程序执行/调度的基本单位。线程是轻量级的线程,共享其所属进程的资源(代码段、堆、静态区等)。若没有线程,当进程 A A A 的任务 1 阻塞时,需要拷贝进程A当前的数据及运行状态,新创建一个进程 A ′ A' A 来执行任务 2 ,相较于使用线程来说内存的开销更大,并且在进程间切换的开销也大于线程切换的开销。

线程基本操作Thread

创建一个线程

thread ThreadName(ThreadInit)
  • 1

传入线程的入口函数 T h r e a d I n i t ThreadInit ThreadInit 来创建一个线程 T h r e a d N a m e ThreadName ThreadName。也可以使用类/对象(需重写 operator())、智能指针、带参的方法、类的成员函数或是 lambda 表达式创建一个线程。

join

thread.join()。会阻塞主线程直到当前线程执行结束后子线程与主线程汇合。

detach

thread.detach()。分离主线程与子线程,主线程运行结束后子线程会在后台执行,执行完成后由运行时库回收相关资源。若主线程中存在实例对象,则在子线程中会调用复制构造函数构建一个新的实例给子线程,因此主线程运行结束并被销毁时不会影响子线程的运行(引用同理)。

joinable

thread.joinable()。当一个线程 detach 后不可再 join,所以 join 前可以使用 joinable 判断是否可以 join。

多线程中的数据共享与互斥

在多线程并发的过程中,多个线程可能会对一块内存同时读/写,可能会导致结果数据与我们期望的数据不一致的问题,因此我们需要通过线程同步的方式来对其进行控制。

交替打印foobar
此处使用一道力扣上的多线程题目作为例题。

给你一个类:

class FooBar {
  public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
    }
  }

  public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

两个不同的线程将会共用一个 FooBar 实例:

线程 A 将会调用 foo() 方法,而
线程 B 将会调用 bar() 方法
请设计修改程序,以确保 “foobar” 被输出 n 次。

示例 1:

输入:n = 1
输出:"foobar"
解释:这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。
  • 1
  • 2
  • 3

示例 2:

输入:n = 2
输出:"foobarfoobar"
解释:"foobar" 将被输出两次。
  • 1
  • 2
  • 3

依题意需要控制调用两线程的运行顺序,达到控制foo和bar交替输出的效果。

互斥锁

设置一互斥锁后可以使用lock()函数给临界区加锁,并且只有一个线程能获得锁,若当前线程无法上锁,则阻塞;反之会在加锁后继续运行。

此处对 foo 和 bar 分别设置一互斥锁,初始化时对 bar 上锁,因此输出 foo 的线程可以执行,而输出 bar 的线程被阻塞。输出完成 foo 后只打开 bar 的锁,foo 仍处于锁定状态;bar 同理。

class FooBar {
private:
    int n;
    mutex fooLock, barLock;

public:
    FooBar(int n) {
        this->n = n;
        barLock.lock();
    }

    void foo(function<void()> printFoo) {
        
        for (int i = 0; i < n; i++) {
            fooLock.lock();
        	printFoo();
            barLock.unlock();
        }
    }

    void bar(function<void()> printBar) {
        
        for (int i = 0; i < n; i++) {
            barLock.lock();
        	printBar();
            fooLock.unlock();
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

也可以使用 try_lock() 尝试上锁,但 try_lock() 不会阻塞线程,若无法加锁,则返回 false,反之返回true。

lock_guard()

类似于智能指针,使用了 RAII 技术,不需要手动 unlock,等离开作用域后就会自动释放。

recursive_lock() 递归互斥锁

允许同一线程多次获得互斥锁

timed_mutex() 超时独占互斥锁

获取互斥锁时若超出超时时长,则放弃等待,解除阻塞。

unique_lock()

比 lock_guard 效率更低,内存占用更大,但比 unique_lock() 更灵活。同时在第二个参数中指定 adopt_lock(),则创建锁时不会再调用构造函数上锁(但这个锁必须要被锁定过),而仍会调用析构函数解锁。

信号量

#include<semaphore.h>
class FooBar {
private:
    int n;
    sem_t mutex_foo, mutex_bar;
public:
    FooBar(int n) {
        this->n = n;
        sem_init(&mutex_foo, 0, 1);
        sem_init(&mutex_bar, 0, 0);
    }

    void foo(function<void()> printFoo) {
        
        for (int i = 0; i < n; i++) {
            sem_wait(&mutex_foo);
        	printFoo();
            sem_post(&mutex_bar);
        }
    }

    void bar(function<void()> printBar) {
        
        for (int i = 0; i < n; i++) {
            sem_wait(&mutex_bar);
        	printBar();
            sem_post(&mutex_foo);
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

原子操作

class FooBar {
private:
    int n;
    atomic<bool> foo_done=false;
public:
    FooBar(int n) {
        this->n = n;
    }

    void foo(function<void()> printFoo) {
        
        for (int i = 0; i < n; i++) {
            while(foo_done) {
                this_thread::yield();
            }
        	printFoo();
            foo_done = true;
        }
    }

    void bar(function<void()> printBar) {
        
        for (int i = 0; i < n; i++) {
            while(!foo_done) {
                this_thread::yield();
            }
        	printBar();
            foo_done = false;
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

条件变量

class FooBar {
private:
    int n;
    mutex lock;
    condition_variable cond;
    bool foo_done = false;
public:
    FooBar(int n) {
        this->n = n;
    }

    void foo(function<void()> printFoo) {
        
        for (int i = 0; i < n; i++) {
            unique_lock<mutex> locker(lock);
            cond.wait(locker, [&](){return foo_done == false;});
        	printFoo();
            foo_done = true;
            cond.notify_one();
        }
    }

    void bar(function<void()> printBar) {
        for (int i = 0; i < n; i++) {
            unique_lock<mutex> locker(lock);
            cond.wait(locker, [&](){return foo_done;});
        	printBar();
            foo_done = false;
            cond.notify_one();
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/1014291
推荐阅读
相关标签
  

闽ICP备14008679号