C++ 多线程学习笔记

std::thread

class thread;:Class to represent individual threads of execution.

void callback1() {
	cout << "hello, callback1" << endl;
}

void callback2(int i) {
	cout << "hello, callback2, i = "<< i << endl;
}

int main() {
	//空参的回调函数
	thread t1(callback1);
	//带有一个参数的回调函数
	thread t2(callback2, 9527);
	t1.join();	//必须回收,否则程序会崩溃
	t2.join();
	return 0;
}

设置线程分离:

void callback() {
	std::cout << "hello,callback" << std::endl;
}
int main() {
	std::thread t1(callback);
	t1.detach();		//设置线程分离
	this_thread::sleep_for(chrono::seconds(1));
	return 0;
}

通过std::thread创建的线程是不可以复制的,但是可以使用std::move()移动

int main() {
	std::thread t1([](){std::cout << "hello, callback" << std::endl;});
	std::thread t2(std::move(t1));

	t2.join();
	return 0;
}

注意在为线程回调传递引用参数时必须用 ref 转化,因为 thread 的构造函数接受的参数是右值引用类型的:

template <class Fn, class... Args>explicit thread (Fn&& fn, Args&&... args);
void foo(int& x) {
    x += 1;
}

int main() {
    int a = 1;
    thread t(foo, ref(a));
    t.join();
    cout << a << endl;
    return 0;
}

关于 ref()C++ std::ref 详解

关于 C++ 中的右值和右值引用:

C++11右值引用(一看即懂)

深入浅出 C++ 11 右值引用

std::this_thread

this_thread 是一个命名空间,其包含如下 4 个工具函数:

thread::id get_id() noexcept;

void yield() noexcept;	// Yield to other threads

template <class Rep, class Period>
void sleep_for (const chrono::duration<Rep,Period>& rel_time);

template <class Clock, class Duration>
void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);

chrono 是一个 C++ 的时间库,seconds 是一个类模板的一种实例化(是一个类):

typedef duration < /*see rep below*/ > seconds;

duration 是一个类模板:

template <class Rep, class Period = ratio<1> >class duration;

其中,ratio 是一个表示分数的类模板:

template <intmax_t N, intmax_t D = 1> class ratio;

typedef std::ratio<1,3> one_third;		// 1/3
std::cout << "one_third= " << one_third::num << "/" << one_third::den << std::endl;

命名空间 this_thread 提供了 get_id() 函数用以获取线程id:

cout << this_thread::get_id() << endl;			//获取当前线程id
cout << t1.get_id() << endl;					//获取t1的id

std::mutex

class mutex;:用于加解锁的互斥类

创建五个子线程,模拟竞争进入临界区的情况:

void callback() {
	std::thread::id id = std::this_thread::get_id();
	std::cout <<id<<" entrying..." << std::endl;
	std::cout <<id<<" doing work" << std::endl;
	std::cout <<id<<" leveling..." << std::endl;
}

int main() {
	std::vector<std::thread> tv;
	for (int i = 0; i < 5; ++i) {
		tv.push_back(std::thread(callback));
	}
	
	for (int i = 0; i < 5; ++i) {
		tv[i].join();
	}
	return 0;
}

可以看出线程的执行循序杂乱无章:

1484 entrying...
3528 entrying...
3528 doing work
3528 leveling...
1484 doing work
1484 leveling...
10096 entrying...
10096 doing work
9868 entrying...
9868 doing work
9868 leveling...
10096 leveling...
4408 entrying...
4408 doing work
4408 leveling...

使用锁std::mutex进行线程互斥:

void callback() {
	g_lock.lock();
	std::thread::id id = std::this_thread::get_id();
	std::cout <<id<<" entrying..." << std::endl;
	std::cout <<id<<" doing work" << std::endl;
	std::cout <<id<<" leveling..." << std::endl;
	g_lock.unlock();
}

看起来输出有序了,但是这样做是不安全的,如果在解锁之前函数因为某种原因异常退出没有解锁,会导致后续线程无法再进入临界区

解决方案是采用lock_guard,利用Resource Acquisition Is Initialization特性

std::lock_guard

类模板:template <class Mutex> class lock_guard;

下面是一个 std::lock_guard 的模拟实现:

template<class Mutex> class LockGuard {
public:
	using mutex_type=Mutex;

	//构造时即加锁
	explicit LockGuard(Mutex& mtx) : m_mutex(mtx) {
		m_mutex.lock();
	}
	//析构时解锁
	~LockGuard() noexcept{
		m_mutex.unlock();
	}
	//禁止拷贝
	LockGuard(const LockGuard&) = delete;
	//LockGuard& operater=(const LockGuard&) = delete;

private:
	Mutex& m_mutex;
};

std::mutex g_lock;

void callback() {
	LockGuard<std::mutex> lock(g_lock);
	std::thread::id id = std::this_thread::get_id();
	std::cout <<id<<" entrying..." << std::endl;
	std::cout <<id<<" doing work" << std::endl;
	std::cout <<id<<" leveling..." << std::endl;
	//离开作用域,lock被析构,自动解锁
}

当然,lock_gurad是一个提供好的类,直接调用即可无需自己实现:

std::lock_guard<std::mutex> lock(g_mutex);   //加锁

std::atomic

类模板:Objects of atomic types contain a value of a particular type (T)

用于定义一个原子化(线程安全)的类型。例如下面的代码原本的 int n = 0 是不安全的,改为 atomic<int> n = 0 后线程安全:

// int n = 0;
atomic<int> n = 0;

void count10000() {
    for (int i = 0; i < 10000; i++)
        n++;
}

int main() {
    thread ths[100];
    for(thread& t : ths) {
        t = thread(count10000);
    }
    for(thread& t : ths) {
        t.join();
    }
    cout << n << endl;
    return 0;
}

std::async

函数模板:

template <class Fn, class... Args> 
future<typename result_of<Fn(Args...)>::type>
async(launch policy, Fn&& fn, Args&&... args);
int adder(int x, int y) {
    this_thread::sleep_for(chrono::seconds(1));
    return x + y;
}

int main() {
    int a{10}, b{20};
    auto f = async(launch::async, adder, a, b);
    cout << f.get() << endl;
    return 0;
}

async 是一个函数模板,它的第一个参数是启动策略,它控制 std::async 的异步行为,我们可以用三种不同的启动策略来创建std::async

  • std::launch::async:保证异步行为,即传递函数将在单独的线程中执行
  • std::launch::deferred:当其他线程调用get()来访问共享状态时,将调用非异步行为
  • std::launch::async | std::launch::deferred:默认行为。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载,但我们无法控制它

第二个参数传入一个可调用对象,后面传入可调用对象的参数,跟 thread() 类似

async() 的返回类型是 future,可以通过它的 get() 方法阻塞等待线程的执行结果

std::future

类模板:

template <class T>  future;
template <class R&> future<R&>;
bool is_prime (int x) {
    for (int i=2; i<x; ++i) if (x%i==0) return false;
    return true;
}

int main () {
    // call function asynchronously:
    std::future<bool> fut = std::async(is_prime, 444444443);

    // do something while waiting for function to set future:
    std::cout << "checking, please wait;";
    std::chrono::milliseconds span (100);
    std::string s = "|/-\\";
    int i = 0;
    while (fut.wait_for(span) == std::future_status::timeout)
        std::cout << "\b" << s[(i++) % 4] << std::flush;
    bool x = fut.get();     // retrieve return value
    std::cout << "\n444444443" << (x?"is":"is not") << " prime\n";
    return 0;
}

wait_for(): Wait for ready during time span

template <class Rep, class Period>
future_status wait_for(const chrono::duration<Rep,Period>& rel_time) const;

可能的返回值有如下 3 种:

在这里插入图片描述
timeout 表示等待指定时长后任务尚未完成

参考文章:

C++11 多线程(std::thread)详解

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值