c++11 标准库,启动线程的接口有如下:
1. std::async();
2. std::packaged_task
3. std::thread
线程不安全, 在什么情况发生:
1. Unsynchronized data access(未同步化的数据访问):并行运行的两个线程读和写同一笔数据,不知道哪一个语句先来。
2.Half-written data(写至一半的数据):某个线程正在读数据, 另一个线程改动它, 于是读取中的线程甚至可能读到改了一半的数据,读到一个半新半旧值。
3.Reordered statement(重新安排的语句):语句和操作有可能被重新安排次序(reordered),也许对于每一个单线程正确, 但对于多个线程的组合却破坏了预期的行为。
解决线程同步安全问题所需要的性质:
1. Atomicity(不可切割性):这意味着读或写一个变量, 其行为是独占的,排他的, 无任何打断,因此一个线程不可能读到“因另一线程而造成的”中间状态。
2.Order(次序): 我们需要一些方法保证“具体指定之语句”的次序。
标准库解决线程同步化与并发所提供的工具有:
1. Mutex 和 Lock: 包括符合RAII 特性的 std::lock_guard; 递归的 std::recursive_mutex ;等等,具体看下面代码示例。
2.std::condition_variable (条件变量):适合用于不同线程执行的task必须彼此等待的情形, 具体看下面代码示例。
3.std::atomic: 保证提供了顺序一致性。 示例如下。
// 多线程TEST.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <random>
#include <iostream>
#include <exception>
#include <Windows.h>
#include <condition_variable>
#include <queue>
using namespace std;
void doSomething(char c) {
default_random_engine dre(c);
uniform_int_distribution<int> id(10,1000);
for (int i = 0; i < 10; i++) {
this_thread::sleep_for(chrono::milliseconds(id(dre)));
cout.put(c).flush();
}
}
class X {
public:
void mem(int num) {
cout << num << endl;
}
};
void test1() {
std::cout << "Starting 2 opreator asyncchronously !\n";
//
//std::future<void> f1 = async([] {doSomething('.'); });
char c = '@';
std::future<void> f1 = async(doSomething, std::ref(c));
std::future<void> f2 = async([] {doSomething('+'); });
//
if (f1.wait_for(chrono::seconds(0)) != future_status::deferred ||
f2.wait_for(chrono::seconds(0)) != future_status::deferred)
{
//poll unti at least one of the loops finished
while (f1.wait_for(chrono::seconds(0)) != future_status::ready &&
f2.wait_for(chrono::seconds(0)) != future_status::ready) {
this_thread::yield();
}
}
cout.put('\n').flush();
X x;
auto a = std::async(&X::mem, x, 23);
try {
f1.get();
// f1.get();只能调用一次get,否则会异常
f2.get();
}
catch (const exception& e) {
cout << "\n EXCREPTION:" << e.what() << endl;
}
cout << "\n done" << endl;
}
/// <summary>
/// /////////////////////
/// </summary>
/// <returns></returns>
int queryNumber() {
cout << "read number:";
int num;
cin >> num;
if (!cin) {
throw runtime_error("no number read");
}
return num;
}
void doSomething2(char c, shared_future<int> f) {
try {
int num = f.get();
for (int i = 0; i < num; i++) {
this_thread::sleep_for(chrono::milliseconds(1000));
cout.put(c).flush();
}
}
catch (const exception& e) {
cerr << "Exception in thread" << this_thread::get_id() << ":" << e.what() << endl;
}
}
void doSomething3(char c, shared_future<int>& f) {
try {
int num = f.get();
for (int i = 0; i < num; i++) {
this_thread::sleep_for(chrono::milliseconds(1000));
cout.put(c).flush();
}
}
catch (const exception& e) {
cerr << "Exception in thread" << this_thread::get_id() << ":" << e.what() << endl;
}
}
void test2() {
try
{
shared_future<int> f = async(queryNumber);
future<void> f1 = async(launch::async, doSomething2, '.', f);
future<void> f2 = async(launch::async, doSomething2, '+', f);
future<void> f3 = async(launch::async, doSomething3, '@', std::ref(f));
f1.get();
f2.get();
f3.get();
}
catch (const std::exception& e)
{
cout << "\n EXCEPTION :" << e.what() << endl;
}
cout << "\n done" << endl;
}
/// <summary>
///
/// </summary>
/// <param name="num"></param>
/// <param name="c"></param>
void doSomething4(int num, char c) {
try {
default_random_engine dre(42*c);
uniform_int_distribution<int> id(10,1000);
for (int i = 0; i < num; ++i) {
this_thread::sleep_for(chrono::milliseconds(id(dre)));
cout.put(c).flush();
}
}
catch (const exception& e) {
cerr << "Exception in thread" << this_thread::get_id() << ":" << e.what() << endl;
}
catch (...) {
cerr << "Exception in thread" << this_thread::get_id();
}
}
void test3() {
try
{
thread t1(doSomething4, 10, '.');
cout << "-started fg thread:" << t1.get_id() << endl;
for (size_t i = 0; i < 5; i++)
{
thread t(doSomething4, 5, 'a' + i);
cout << "-deatch started bg thread" << t.get_id() << endl;
t.detach();//很少使用, 危险, 退出程序, 线程仍然可能还在运行。
}
cin.get();
cout << "-join fg thread" << t1.get_id() << endl;
t1.join();
cout << "-exit" << endl;
}
catch (const std::exception& e)
{
cerr << "exception :" << e.what() << endl;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
///
void doSomething5(std::promise<std::string>& p ) {
try {
cout << "read char('x' for exception):";
char c = std::cin.get();
if (c == 'x') {
throw std::runtime_error(std::string("char ")+c+" read");
}
std::string s = std::string("char ") + c + " processed";
p.set_value(std::move(s));
}
catch (...) {
//cerr << "Exception in thread" << this_thread::get_id();
p.set_exception(std::current_exception());
}
}
void test4() {
try
{
std::promise<string> p;
std::thread t(doSomething5, std::ref(p));
t.detach();
//
std::future<std::string> f(p.get_future());
//
std::cout << "result " << f.get() << std::endl;
}
catch (const std::exception& e)
{
std::cerr << "EXCEPTION: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "EXCEPTION: " << std::endl;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
double compute(int x, int y) {
cout << "start thread id:" << this_thread::get_id()<<endl;
this_thread::sleep_for(chrono::milliseconds(500));
return (x + y);
}
void test5() {
std::packaged_task<double(int, int)> task(compute);
std::future<double> f = task.get_future();
//
cin.get();
cout << "pepare start"<<endl;
task(5, 6);
double res = f.get();
cout << "res compute(5,6)=" << res << endl;
}
//
std::mutex printMutex;
void print(const std::string& s) {
std::lock_guard<std::mutex> l(printMutex);
for (char c:s)
{
std::cout.put(c);
}
std::cout << std::endl;
}
void test6() {
auto f1 = std::async(std::launch::async, print, "hello from a first thread");
auto f2 = std::async(std::launch::async, print, "hello from a second thread");
print("hell from the main thread");
}
//递归锁
class DatabaseAccess {
private :
std::recursive_mutex dbMutex;
public:
void createTable() {
std::lock_guard<std::recursive_mutex> lg(dbMutex);
std::cout << "create table" << endl;
}
void insertData() {
std::lock_guard<std::recursive_mutex> lg(dbMutex);
std::cout << "insert data table" << endl;
}
void createTableAndInsertData() {
std::lock_guard<std::recursive_mutex> lg(dbMutex);
createTable();
insertData();
std::cout << "createTableAndInsertData" << endl;
}
};
void test7() {
DatabaseAccess db;
db.createTableAndInsertData();
}
//
//
class DatabaseAccess1 {
private:
// std::timed_mutex dbMutex;
std::recursive_timed_mutex dbMutex;
public:
void createTable() {
// std::lock_guard<std::recursive_mutex> lg(dbMutex);
if (dbMutex.try_lock_for(chrono::milliseconds(500))) {
std::lock_guard<std::recursive_timed_mutex> lg(dbMutex, std::adopt_lock);
std::cout << "create table" << endl;
}
else {
std::cout << "try lock failed createTable" << endl;
}
}
void insertData() {
if (dbMutex.try_lock_for(chrono::milliseconds(500))) {
std::lock_guard<std::recursive_timed_mutex> lg(dbMutex, std::adopt_lock);
std::cout << "insert data table" << endl;
}
else {
std::cout << "try lock failed insertData" << endl;
}
}
void createTableAndInsertData() {
if (dbMutex.try_lock_for(chrono::milliseconds(500))) {
std::lock_guard<std::recursive_timed_mutex> lg(dbMutex, std::adopt_lock);
createTable();
insertData();
std::cout << "createTableAndInsertData" << endl;
}
else {
std::cout << "try lock failed createTableAndInsertData" << endl;
}
}
};
void test8() {
DatabaseAccess1 db;
db.createTableAndInsertData();
}
//
void test9() {
std::mutex m1;
std::mutex m2;
int idx = std::try_lock(m1, m2);
if (idx < 0) {
std::lock_guard<mutex> lg1(m1, std::adopt_lock);
std::lock_guard<mutex> lg2(m2, std::adopt_lock);
cout << "lock success" << endl;
}
else {
std::cerr << "coult not lock " << endl;
}
}
//
void initialized1() {}
void test10() {
bool initialized = false;
std::once_flag oc;
std::call_once(oc, initialized1 );
}
//多线程环境中,缓式初始化
class B {
private:
mutable std::once_flag initDataFlag;//mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
void initData() const {}
public:
string getData() const {
std::call_once(initDataFlag, &B::initData, this);
return "success";
}
};
//条件变量 condition_variable
namespace conditionTest {
bool readyFlag;
std::mutex readyMutex;
std::condition_variable readyCondVar;
void thread1() {
std::cout << "<return>" << std::endl;
std::cin.get();
{
std::lock_guard<std::mutex> lg(readyMutex);
readyFlag = true;
}
readyCondVar.notify_one();
}
void thread2() {
{
std::unique_lock<std::mutex> ul(readyMutex);
readyCondVar.wait(ul, [] {return readyFlag; });
}
std::cout << "done" << std::endl;
}
}
void test11() {
auto f1 = std::async(std::launch::async, conditionTest::thread1);
auto f2 = std::async(std::launch::async, conditionTest::thread2);
}
//
namespace conditionTest2 {
std::queue<int> queue;
std::mutex queueMutex;
std::condition_variable queueCondVar;
//
void provider(int val) {
for (int i=0;i<6;++i)
{
std::lock_guard<std::mutex> lg(queueMutex);
queue.push(val+i);
std::cout << "provider:" << val + i << endl;
}
queueCondVar.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(val));
}
//
void consumer(int num) {
while (true) {
int val;
{
std::unique_lock<std::mutex> ul(queueMutex);
queueCondVar.wait(ul, [] {return !queue.empty(); });
val = queue.front();
queue.pop();
/// std::cout << "consumer:" << val<<endl;
}
std::cout << "consumer:" << num<<":"<<val << endl;
}
}
}
void test12() {
auto p1 = std::async(std::launch::async, conditionTest2::provider, 100);
auto p2 = std::async(std::launch::async, conditionTest2::provider, 300);
auto p3 = std::async(std::launch::async, conditionTest2::provider, 500);
//
auto c1 = std::async(std::launch::async, conditionTest2::consumer, 1);
auto c2 = std::async(std::launch::async, conditionTest2::consumer, 2);
}
//atomicity
namespace AtomicityTest {
long data;
std::atomic<bool> readyFlag(false);
void provider() {
cout << "<return>" << endl;
std::cin.get();
data = 42;
readyFlag.store(true);
}
void consumer() {
while (!readyFlag.load()) {
std::cout.put('.').flush();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
cout << "\nvalue:" << data << std::endl;
}
}
void test13() {
auto p = std::async(std::launch::async, AtomicityTest::provider);
auto c = std::async(std::launch::async, AtomicityTest::consumer);
}
int main()
{
//test1();
//test2();
//test3();
//test4();
//test5();
//test6();
// test7();
// test8();
//test9();
//test11();
//test12();
test13();
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件