乐趣区

c1114多线程专题

在 C ++11 以前,C++ 的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性。C++11 中,引入了 boost 库中的多线程部分内容,形成 C ++ 标准,形成标准后的 boost 多线程编程部分接口基本没有变化,这样方便了以前使用 boost 接口开发的使用者切换使用 C ++ 标准接口,很容易把 boost 接口升级为 C ++ 标准接口。

我们通过如下几部分介绍 C ++11 多线程方面的接口及使用方法。

1. std::thread

std::thread 为 C ++11 的线程类,使用方法和 boost 接口一样,非常方便,同时,C++11 的 std::thread 解决了 boost::thread 中构成参数限制的问题,我想这都是得益于 C ++11 的可变参数的设计风格。

我们通过如下代码熟悉下 std::thread 使用风格:

//c11.cpp
#include <iostream>
#include <thread>
void threadfun1()
{
    std::cout << "threadfun1 - 1\r\n" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "threadfun1 - 2" << std::endl;
}
void threadfun2(int iParam, std::string sParam)
{
    std::cout << "threadfun2 - 1" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "threadfun2 - 2" << std::endl;
}
int main()
{std::thread t1(threadfun1);
    std::thread t2(threadfun2, 10, "abc");
    t1.join();
    std::cout << "join" << std::endl;
    t2.detach();
    std::cout << "detach" << std::endl;
}

注意编译时要使用:g++ c11.cpp -lpthread
运行结果:

threadfun1 - 1
threadfun2 - 1
threadfun1 - 2
join
detach

2. std::atomic

std::atomic 为 C ++11 封装的原子数据类型。
什么是原子数据类型?从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上来看,我们可以理解为这些原子类型内部自己加了锁。

我们下面通过一个测试例子说明原子类型 std::atomic<int> 的特点。

我们使用 10 个线程,把 std::atomic<int> 类型的变量 iCount 从 10 减到 1。

//c11.cpp
#include <thread>
#include <atomic>
#include <stdio.h>
#include <iostream>
#include <list>
std::atomic<bool> bIsReady(false);
std::atomic<int> iCount(10);
void threadfun1()
{if (!bIsReady) {std::this_thread::yield();
    }
    while (iCount > 0)
    {printf("iCount:%d\r\n", iCount--);
    }
}
int main()
{
    std::list<std::thread> lstThread;
    for (int i = 0; i < 10; ++i)
    {lstThread.push_back(std::thread(threadfun1));
    }
    for (auto& th : lstThread)
    {th.join();
    }
}

运行结果:

iCount:10
iCount:9
iCount:8
iCount:7
iCount:6
iCount:5
iCount:4
iCount:3
iCount:2
iCount:1

从上面的结果可以看到,iCount 的最小结果是 1,没有出现小于等于 0 的情况,大家可以把 iCount 改成 100 甚至 1000 看看,可能会更直观一点。

3. std::condition_variable

C++11 中的 std::condition_variable 就像 Linux 下使用 pthread_cond_wait 和 pthread_cond_signal 一样,可以让线程休眠,直到被唤醒,然后再重新执行。线程等待在多线程编程中使用非常频繁,经常需要等待一些异步执行的条件的返回结果。
代码如下:

#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {std::unique_lock<std::mutex> lck(mtx);
    while (!ready) cv.wait(lck); // 线程将进入休眠
    // ...
    std::cout << "thread" << id << '\n';
}
void go() {std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_all();}
int main()
{std::thread threads[10];
    // spawn 10 threads:
    for (int i = 0; i<10; ++i)
        threads[i] = std::thread(print_id, i);
    std::cout << "10 threads ready to race...\n";
    go(); // go!
    for (auto& th : threads) th.join();
    return 0;
}

运行结果:

10 threads ready to race...
thread 0
thread 1
thread 2
thread 3
thread 4
thread 5
thread 6
thread 7
thread 8
thread 9

上面的代码,在调用 go 函数之前,10 个线程都处于休眠状态,当 cv.notify_all() 运行后,线程休眠结束,继续往下运行,最终输出如上结果。

cpp 加油站 (ID:xy13640954449), 作者 linux 服务器开发老司机,欢迎来撩!

退出移动版