• C++11并发编程个人小结


    thread_local变量在每个线程第一次执行到时初始化(类似static),并在每个线程各自累加,并在线程结束时释放。

    std::condition_variable::
      wait(std::unique_lock<std::mutex>& lock, Predicate pred);
        pred为true时直接返回;
        pred为false时,lock必须满足已被当前线程锁定的前提。执行原子地释放锁定,阻塞当前线程,并将其添加到等待*this的线程列表中。
      void notify_one();
        激活某个等待*this的线程,被激活的线程重新获得锁。

    async:能够保留线程的返回值。启动一个线程,或在期望等待时同步任务.
    std::launch::async表明函数必须在其所在的独立线程上执行
    std::launch::defered表明函数调用被延迟到wait()或get()函数调用时才执行
    如果两者都行,取决于实现;未声明则未定义。

    ####################
    线程相关的很多=默认是move语义。
    thread:.join(), .detach(), .swap()
    mutex:mutex, lock_guard, unique_lock等, .lock(), .unlock()
    condition_variable:.notify_one(), .wait()//实现等待队列
    future:
    async: 类似于thread, 能保留返回值,可启动一个线程,或在期望等待时同步任务.
        std::launch::async表明函数必须在其所在的独立线程上执行
        std::launch::defered表明函数调用被延迟到wait()或get()函数调用时才执行
        如果两者都行,取决于实现;未声明则未定义。
    packaged_task: 构造时绑定一个函数对象,.get_future()将返回值绑定到future,再执行task函数(或传递到std::thread对象中可作为线程函数)
    promise: .getfuture(), 绑定到future; 执行.set_value()后, 绑定的future可.get(), 否则.get()一直被阻塞直到.set_value()执行。
    future: .share(), .wait(), .get()
        当future的状态还不是就绪时就调用绑定的promise, packaged_task等的析构函数,会在期望里存储一个异常。
        std::future有局限性,在很多线程等待时,只有一个线程能获取等待结果。
        当多个线程需要等待相同的事件的结果,需要用shared_future来替代future。
        shared_future与future类似,但shared_future可以拷贝、多个shared_future可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。
        shared_future可通过某个future对象隐式转换,或通过future::share()显示转换,无论哪种转换,被转换的那个future对象都会变为not-valid.

    atomic:
      is_lock_free: 如果某个对象满足 lock-free 特性,在多个线程访问该对象时不会导致线程阻塞。
            (其实看我们那副图就是说你的各个线程不会互相阻塞,
            那么你的程序才能成为lock free的。
            像我们平常用的互斥锁,当有线程获得锁,
            其他线程就被阻塞掉了,
            这里的问题就是如果获得锁的线程挂掉了,
            而且锁也没有释放,那么整个程序其实就被block在那了,
            而如果程序是lock free的那么即使有线程挂掉,
            也不影响整个程序继续向下进行,
            也就是系统在整体上而言是一直前进的。
            大概系统内部自有一套判断机制)
      store: 修改被封装的值
      load: 读取被封装的值

    内存模型:
      要想写出高性能的多线程程序必须理解内存模型,
      因为编译器会给你的程序做优化(如指令重排等),
      CPU为了提升性能也有多发射和乱序执行,
      因此程序在最终执行时并不会按照你之前的原始代码顺序来执行,
      所以内存模型是程序员、编译器,CPU 之间的契约,遵守契约后大家就各自做优化,从而尽可能提高程序的性能。
      _relaxed: 不对执行顺序做任何保证
      _acquire: 本线程中,所有后续的读操作必须在本原子操作完成后执行
      _release: 本线程中,所有之前的写操作完成后才能执行本原子操作
      _acq_rel: 同时包含acquire和release
      _consume: 本线程中,所有后续的有关本原子类型的操作,必须在本原子操作完成后执行
      _seq_cst: 全部存取按顺序执行

     =========================================================

    PV原语: 信号量s, 除一个整数值s.cound, 还有一个进程等待队列s.queue, 是阻塞在该信号上的进程。

    信号量的值为非负值表示当前空闲资源数, 负值表示当前阻塞在该信号上的进程。

    P(Semaphore s) {--s.count; if(s.count < 0) 阻塞调用进程;}

    V(Semaphore s) {++s.count; if(s.count <= 0) 唤醒某一进程;}

    互斥量mutex就是count = 1的PV原语。

    如何实现同步?考虑进程的DAG图, A进程的某个节点a有指向B进程的某个节点b, 那么相当于一开始b处于阻塞态, A进程到a处执行V原语释放资源。如公交司机开车停车与售票员开门关门。 

    经典算法

    生产者与消费者:(以下代码每个生产者生产数量与每个消费者消费数量相同)

     1 /***************
     2 首先你要有一个生产者和消费者公用的Buffer, 它要求用mutex和condition variable来实现锁
     3 一个互斥量, 互斥地访问buffer, 多个消费者和生产者每个消费和生产的数量相同。
     4 本写法本质为单消费者单生产者。
     5 正确写法:
     6 单生产者单消费者: 设置一个互斥量mtx, 互斥地访问buffer; 两个条件变量表示buffer满或空
     7 多生产者单消费者: 再规定总生产量为count_produce, 多生产者互斥地访问count_produce
     8 多生产者单消费者: 再规定总消费量为count_consume, 多消费者互斥地访问count_consume
     9 多生产者单消费者: 再规定总生产量为count_produce, 多生产者互斥地访问count_produce, 规定总消费量为count_consume(count_consume = count_produce), 多消费者互斥地访问count_consume
    10 ***************/
    11 #include <bits/stdc++.h>
    12 struct BoundedBuffer {
    13     int* buffer;
    14     int capacity;
    15 
    16     int front;
    17     int rear;
    18     int count;
    19 
    20     std::mutex lock;
    21 
    22     std::condition_variable not_full;
    23     std::condition_variable not_empty;
    24 
    25     BoundedBuffer(int capacity) : capacity(capacity), front(0), rear(0), count(0) {
    26         buffer = new int[capacity];
    27     }
    28     ~BoundedBuffer(){
    29         delete[] buffer;
    30     }
    31     void deposit(int data){
    32         std::unique_lock<std::mutex> l(lock);
    33         not_full.wait(l, [this](){return count != capacity; });
    34         buffer[rear] = data;
    35         rear = (rear + 1) % capacity;
    36         ++count;
    37         not_empty.notify_one();
    38     }
    39     int fetch(){
    40         std::unique_lock<std::mutex> l(lock);
    41         not_empty.wait(l, [this](){return count != 0; });
    42         int result = buffer[front];
    43         front = (front + 1) % capacity;
    44         --count;
    45         not_full.notify_one();
    46         return result;
    47     }
    48 };
    49 
    50 
    51 void consumer(int id, BoundedBuffer& buffer){
    52     for(int i = 0; i < 50; ++i){
    53         int value = buffer.fetch();
    54         std::cout << "Consumer " << id << " fetched " << value << std::endl;
    55         std::this_thread::sleep_for(std::chrono::milliseconds(250));
    56     }
    57 }
    58 
    59 void producer(int id, BoundedBuffer& buffer){
    60     for(int i = 0; i < 75; ++i){
    61         buffer.deposit(i);
    62         std::cout << "Produced " << id << " produced " << i << std::endl;
    63         std::this_thread::sleep_for(std::chrono::milliseconds(100));
    64     }
    65 }
    66 
    67 int main(){
    68     BoundedBuffer buffer(200);
    69 
    70     std::thread c1(consumer, 0, std::ref(buffer));
    71     std::thread c2(consumer, 1, std::ref(buffer));
    72     std::thread c3(consumer, 2, std::ref(buffer));
    73     std::thread p1(producer, 0, std::ref(buffer));
    74     std::thread p2(producer, 1, std::ref(buffer));
    75 
    76     c1.join();
    77     c2.join();
    78     c3.join();
    79     p1.join();
    80     p2.join();
    81 
    82     return 0;
    83 }
    View Code

     参考链接

    读者写者问题: 参考链接

    哲学家进餐问题: 1.最多允许四个人同时在进餐 2.当两边筷子都可用时再去拿 3.每次先拿奇数号的筷子再拿偶数号的筷子 4.分成三种状态, 思考, 饥饿, 进餐, 并且一次拿一双

  • 相关阅读:
    Java 常见异常种类
    Spring3.2+mybatis3.2+Struts2.3整合配置文件大全
    Java中的基本类型和引用类型变量的区别
    【git】Git 提示fatal: remote origin already exists 错误解决办法
    【Java集合】Java中集合(List,Set,Map)
    POJ3322-经典的游戏搜索问题
    程序人生[流程图]
    不使用中间变量交换两个数
    做人要低调,别把自己太当回事
    【转】嵌套子控件设计时支持
  • 原文地址:https://www.cnblogs.com/dirge/p/6679662.html
Copyright © 2020-2023  润新知