c++11中增加了线程以及线程相关的类,很方便的支持了并发编程。

1. 线程

 
    使用std::thread创建线程,提供线程函数或者函数对象即可,并且可以指定线程函数的参数。

#include<thread>
void func(int a){
    cout << "param= " << a << endl;
}
int main(){
    std::thread t(func, 10); //创建线程使用线程函数以及函数参数
    t.join(); //等待线程函数执行完毕
    return 0;
}

 

    可以通过std::bind或lambda表达式来创建线程:

void func(int a,int b){
    cout << "a = " << a << ", b = " << b << endl;
}
int main(){
    std::thread t1(std::bind(func, 1, 2));
    std::thread t2([](int a, int b){cout << "a = " << a << ", b = " << b << endl;}, 1,2);
    return 0;
}

 

 
    若在创建子线程的线程中,既没有执行对子线程进行join也没有进行detach,则有可能出现线程对象在线程函数结束之前就失效,从而出错。

int main(){
    std::thread t(func);
    t.detach();
    //做其他事情....
    return 0;
}

 

 
    线程不能被复制,但可以移动,比如:

#include<thread>
void func(){
    //do some work
}
int main(){
    std::thread t(func);
    std::thread t1(std::move(t));
    t.join();
    t1.join();
    return 0;
}//线程被移动(std::move)后,线程对象t就不代表任何线程了。

 

 
1. 获取当前信息

void func(){
}
int main(){
    std::thread t(func);
    cout << t.get_id() << endl; //获取线程id
    cout << std::thread::hardware_concurrency() << endl; //获取cpu核数
    return 0;
}

 

2. 线程休眠

void f(){   
    std::this_thread::sleep_for(std::chrono::seconds(3));   //当前线程休眠3秒钟
}

 

2. 互斥量

 

std::mutex

std::mutex g_lock;
void func(){
    g_lock.lock();
    std::cout << "entered thread " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "leave thread " << std::this_thread::get_id() << endl;
    g_lock.unlock();
}

 


 
(3)递归锁虽然允许同一线程多次获得同一个互斥量,可重复获得的最大次数并未具体说明,一旦超过次数,再对lock进行调用就会抛出std::system错误。

 
std::time_mutex 使用 try_for_lock(timexx)来尝试获取锁,在超过timexx时间仍未获得锁,则返回超时。

std::time_mutex mut;
void work(){
    std::chrono::milliseconds timeout(100);
    while(true){
        if(mut.try_lock_for(time_out)){
            std::cout << std::this_thread::get_id() << " :do work with the mutex" << endl;
            std::chrono::milliseconds sleepDuration(250);
            std::this_thread::sleep_for(sleepDuration);
            mut.unlock();
        }else{
            std::cout << "time out for get lock" << endl;
        }
    }
}

 

 
    lock_guard利用RAII机制,防止忘记unlock或者发生异常时无法unlock造成程序死锁。

std::mutex mut;
void func(){
    std::lock_guard<std::mutex> guard(mut);
    xxxx
}

 

3. 条件变量

 
但效率比condition_varaible略低

void Put(const T& x){
    std::lock_guard<std::mutex> locker(m_mutex);
    while(IsFull()){
        cout << "缓冲区满,需要等待..." << endl;
        m_notFull.wait(m_mutex);
    }
    m_queue.push_back(x);
    m_notEmpty.notify_one();
}

void Take(T& x){
    std::lock_guard<std::mutex> locker(m_mutex);
    while(IsEmpty()){
        cout << "缓冲区空,需要等待...." << endl;
        m_notEmpty.wait(m_mutex);
    }
    x = m_queue.front();
    m_queue.pop_front();
    m_notFull.notify_one();
}
std::list<T> m_queue;
std::mutex m_mutex;
std::condition_variable_any m_notEmpty;
std::condition_variable_any m_notFull;

 

4. 原子变量

    c++11提供了一个原子类型 std::atomic< T>,可以使用任意类型作为模板参数,c++11内置了整型的原子变量,可以更方便的使用原子变量,使用原子变量就不需要使用互斥量来保护该变量了。

#include<atomic>
struct AtomicCounter{
    std::atomic<int> value;
    void inc(){
        ++value;
    }
    void dec(){
        --value;
    }
    int get(){
        return value.load();
    }
};

 


5. call_once/once_flag使用

    为了保证在多线程环境中某个函数仅被调用一次,或者某个变量仅仅被初始化一次,可以使用 std::call_once来保证函数在多线程环境中只被调用一次。使用std::call_once时,需要一个once_flag作为call_once的入参。

std::once_flag flag;
void do_once(){
    std::call_once(flag, [](){ std::cout << "called once" << endl;});
}

6. 异步操作

    c++11中提供了异步操作相关的类,主要有 std::future, std::promise, std::package_task. std::future 作为异步结果的传输通道,很方便的获取线程函数的返回值; std::promise用来包装一个值,将数据和future绑定起来,方便线程赋值;std::package_task用来包装一个可调用对象,将函数和future帮顶起来,以便异步调用。

 
future_status == Timeout 异步操作超时

std::future_status st;
do{
    st = future.wait_for(std::chrono::seconds(1));
    if(st == std::future_status::deferred){
        //还没开始
    }else if(st == std::future_status::timeout){
        //超时
    }else if(st == std::future_status::ready){
        //ready
    }
}while(st != std::future_status::Ready);

 

    获得future结果的三种方式: get, wait, wait_for, 其中get等待异步操作的结果并返回结果;wait只是等待异步操作完成,没有返回值;wait_for是超时等待返回的结果。

 
    std::promise 将数据和future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传进来的promise赋值,在线程函数执行完成之后就可以通过promise的future获取该值。

std::promise<int> pr;
std::thread t([](std::promise<int>& p)
{ p.set_value_at_thread_exit(9);},      //在线程函数中为promise赋值
std::ref(pr));  //线程函数的参数,使用引用

std::future<int> f = pr.get_future(); //获取promise的future
auto r = f.get();   //利用get等待异步操作的结果(阻塞)

 

 
    std::packaged_task可以将函数等可调用对象和future绑定起来,以便异步调用。

    std::packaged_task<int()> task([]{return 7;});
    std::thread t1(std::ref(task));
    std::future<int> f1 = task.get_future();
    auto r1 = f1.get();

 

 
    std::future提供了一个访问异步操作结果的机制,它和线程是一个级别的,属于低层次的对象。std::future之上的高一层为 std::packaged_task和std::promise, 他们内部都有future以便访问异步操作的结果,std::packaged_task包装的是一个异步操作,而std::promise包装的是一个值,都是为了方便异步操作。
    future被promise和packaged_task用来作为异步操作或异步结果的连接通道,用std::future和std::shared_future来获取异步调用的结果。future不可拷贝,只能移动,shared_future可以拷贝,当需要将future放到容器中则需要用shared_future。

#include<iostream>
#include<utility>
#include<future>
#include<vector>
#include<thread>

int func(int x){
	return x*x;
}

int main(){
	std::packaged_task<int(int)> tsk(func);
	std::future<int> fut = tsk.get_future();

	std::thread(std::move(tsk), 2).detach();
	
	int value = fut.get();
	std::cout << "The result is " << value << ".\n";

	//std::future不可复制,无法放到容器中,需要用shared_future
	std::vector<std::shared_future<int>> v;
	std::shared_future<int> f = std::async(std::launch::async, [](int a, int b){ return a + b; }, 2, 3);
	v.push_back(f);

	std::cout << "The shared future result is " << v[0].get() << "\n";
	return 0;
}

 

 
std::launch::deferred 延迟加载方式创建线程。调用 async时不创建线程,直到调用了future的get或者wait方法时才创建线程。

#include<iostream>
#include<thread>
#include<future>
#include<utility>
int main(){
	std::future<int> f1 = std::async(std::launch::async, [](){
		return 8;
	});

	std::cout << f1.get() << std::endl;

	std::future<void> f2 = std::async(std::launch::async, [](){
		std::cout << 8 << std::endl;
	});

	f2.wait();

	std::future<int> future = std::async(std::launch::async, [](){
		std::this_thread::sleep_for(std::chrono::seconds(3));
		return 8;
	});

	std::cout << "waiting ... " << std::endl;
	std::future_status status;

	do{
		status = future.wait_for(std::chrono::seconds(1));
		if (status == std::future_status::deferred){
			std::cout << "deferred..\n";
		}else if(status == std::future_status::timeout){
			std::cout << "time out...\n";
		}
		else if (status == std::future_status::ready){
			std::cout << "ready!\n";
		}
	} while (status != std::future_status::ready);
	std::cout << "result is " << future.get() << std::endl;
	return 0;
}

 

相关文章: