c++11并发语法初步

c++11并发语法初步

参考:

https://www.cnblogs.com/haippy/p/3284540.html

https://en.cppreference.com/w/

https://wizardforcel.gitbooks.io/cpp-11-faq/content/77.html

http://www.cplusplus.com/reference/

C++11多线程头文件

C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是 <atomic> , <thread> , <mutex> , <condition_variable> 和 <future> 。 </future> </condition_variable> </mutex> </thread> </atomic>

  • <atomic> :该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。 </atomic>
  • <thread> :该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。 </thread>
  • <mutex> :该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。 </mutex>
  • <condition_variable> :该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。 </condition_variable>
  • <future> :该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。 </future>

多线程的Hello World

#include <bits/stdc++.h>
#include <thread>

using namespace std;

void function_name() {
    cout << "hello world" << endl;
}

int main()
{
    std::thread t(function_name);
    t.join();

    return 0;
}

std::thread 构造

  • (1). 默认构造函数,创建一个空的 thread 执行对象。
  • (2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
  • (3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
  • (4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。

注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.

其他函数

get_id 获取线程ID
joinable 检查线程是否可以join
join join线程
detach detach线程
swap swap线程
native_handle 返回native_handle
hardware_concurrency[static] 检查硬件并发性

参考:https://en.cppreference.com/w/cpp/thread/thread/thread

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
 
void f1(int n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 1 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
void f2(int& n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 2 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
class foo
{
public:
    void bar()
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 3 executing\n";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    int n = 0;
};
 
int main()
{
    int n = 0;
    foo f;
    std::thread t1; // t1 is not a thread
    std::thread t2(f1, n + 1); // pass by value
    std::thread t3(f2, std::ref(n)); // pass by reference
    std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
    std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
    t2.join();
    t4.join();
    t5.join();
    std::cout << "Final value of n is " << n << '\n';
    std::cout << "Final value of foo::n is " << f.n << '\n';
}
Thread 1 executing
Thread 2 executing
Thread 3 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Thread 2 executing
Thread 3 executing
Thread 1 executing
Thread 3 executing
Thread 2 executing
Thread 1 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Final value of n is 5
Final value of foo::n is 5
std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f

保留疑惑,函数名即是地址,加&还是地址,但是看别人怎么写。

move (1) thread& operator= (thread&& rhs) noexcept;
copy [deleted] (2) thread& operator= (const thread&) = delete;
  • (1). move 赋值操作,如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则 terminate() 报错。
  • (2). 拷贝赋值操作被禁用,thread 对象不可被拷贝。

互斥量

Mutex 系列类(四种)

  • std::mutex,最基本的 Mutex 类。
  • std::recursive_mutex,递归 Mutex 类。
  • std::time_mutex,定时 Mutex 类。
  • std::recursive_timed_mutex,定时递归 Mutex 类。

Lock 类(两种)

  • std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。
  • std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

其他类型

  • std::once_flag
  • std::adopt_lock_t
  • std::defer_lock_t
  • std::try_to_lock_t

函数

  • std::try_lock,尝试同时对多个互斥量上锁。
  • std::lock,可以同时对多个互斥量上锁。
  • std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。

并发最大的问题就是对临界区资源的访问,主要是通过互斥量上锁的方法解决。

http://www.cplusplus.com/reference/mutex/mutex/try_lock/ 具体细节看库即可,下面解析一些常见的

主要用unique_lock,在构造函数上锁,在析构函数解锁,第一个参数为互斥量,第二个是可选

  • std::adopt_lock_t
  • std::defer_lock_t
  • std::try_to_lock_t

https://en.cppreference.com/w/cpp/thread/lock_tag

#include <bits/stdc++.h>

using namespace std;

int main()
{

    std::mutex my_mutex;
    std::unique_lock<std::mutex> lock(my_mutex);

    ///表示这个互斥量已经lock成功了,通知构造函数不用在lock了,
    my_mutex.lock();    ///必须先lock才能用
    std::unique_lock<std::mutex> lock1(my_mutex, std::adopt_lock);

    ///后面会自己unlock()


    ///============================
    ///尝试锁,没锁成功不阻塞, 前提互斥量不能被锁定
    std::unique_lock<std::mutex> lock2(my_mutex, std::try_to_lock);
    if(lock.owns_lock()) {
        ///拿到了锁头

    } else {
        ///没有拿到锁头
    }

    ///============================
    ///我没加锁,后面手动锁,方便调用函数, 前提互斥量不能被锁定
    std::unique_lock<std::mutex> lock3(my_mutex, std::defer_lock);
    lock3.lock();   ///加锁

    lock3.unlock(); ///不加锁

    if(lock3.try_lock() == true) {
        ///拿到锁头了
    } else {
        ///没拿到

    }

    ///释放互斥量
    std::mutex *m = lock3.release();

    return 0;
}

std::condition_variable

一个线程等待另一个线程条件满足

void function_name() {
    unique_lock<mutex> lock(mutex1);

    ///能往下走,肯定互斥量默认被锁了,到wait。
    ///第二个参数可选,没写默认false。true为不堵塞,继续走。
    ///如果是false卡在这等待其他线程的con.notify_one()的函数
    ///注意notify_one的时候必须卡在wait的地方,否则唤醒失败
    ///notify_all()
    
    con.wait(lock, [] () {
        if(true) {
            return true;
        }
        return false;
    });
}

std::async, std::future 线程返回值

std::async启动一个异步任务后,返回一个std::future访问异步操作结果的机制

#include <bits/stdc++.h>
#include <future>

using namespace std;

int mythread() {
    cout << "mythread = " << this_thread::get_id() << endl;
    return 5;
}

int main () {

    cout << "main = " << this_thread::get_id() << endl;
    ///压根没创建子线程,在主线程中做,视乎默认是这个
    std::future<int>ret = std::async(std::launch::deferred, mythread);
  ///async 新线程在这里开始
    std::future<int>ret = std::async(std::launch::async, mythread);
    cout << ret.get();  ///卡在这里等结果
    return 0;
}

用packaged_task打包线程,就多了一个可以返回结果的future接口

#include <bits/stdc++.h>
#include <future>
#include <thread>
using namespace std;
int mythread() {
    cout << "mythread = " << this_thread::get_id() << endl;
    return 5;
}
int main () {
    ///std::packaged_task 打包可调用对象
    std::packaged_task<int()>mypt(mythread);
    std::thread t1(std::ref(mypt));
    t1.join();

    std::future<int>res = mypt.get_future();
    return 0;
}

后续就可以这么用

    ///
    vector<std::std::packaged_task<int()>> vec;
    ///不用移动语义可能会出问题
    vec.push_back(std::move(mypt));
全部评论

相关推荐

点赞 1 评论
分享
牛客网
牛客企业服务