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

基本概念

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

线程基本操作Thread

创建一个线程

thread ThreadName(ThreadInit)

传入线程的入口函数 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");
    }
  }
}

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

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

示例 1:

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

示例 2:

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

依题意需要控制调用两线程的运行顺序,达到控制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();
        }
    }
};

也可以使用 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);
        }
    }
};

原子操作

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;
        }
    }
};

条件变量

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
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值