c++ boost多线程学习(一)

本次学习相关资料如下:
Boost C++ 库 第 6 章 多线程(大部分代码的来源)
Boost程序库完全开发指南 - 深入C++“准”标准库 第三版 罗剑锋著

头文件:

#include <stdio.h>
#include <string.h>
#include <boost\version.hpp>
#include <boost\config.hpp>
#include <iostream>
#include <boost\thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#include <cstdlib>
#include <vector>
#include <random>

首先是如何创建简单的多线程:

int sum = 0;

void f_mutex(unsigned int no) {
	for (int i = 0; i < 10; i++) {
		sum++;
		std::cout << "我是线程" << no << ": sum 为" << sum << std::endl;
	}
}

int main()
{
	std::cout << boost::thread::hardware_concurrency() << std::endl;//这个可以用来显示CPU内核数,就是可以并行的线程数量
	std::cout << "程序开始" << std::endl;
	boost::thread a(f_metex,1); //第一个参数是线程要执行的函数,后面是函数所需要的参数
	boost::thread b(f_metex,2);
	a.join(); //等待线程结束,如果没有这个的话主线程一下就结束了,子线程也没了
	b.join();
	return 0;
}

上面的打印可能会出问题,sum的值不一定就是20,因为cpu在任意时刻都有可能切换线程,当1号线程读取出sum的值,准备加1的时候,cpu可能会切换到2号线程,读取出sum的值并加1,然后再切换回1号线程,这时候1号线程加1的值就不正确了。

所以要控制这个共享资源,出现了互斥量这样的内容。

boost::mutex mu; //这个是互斥量

void f_mutex(unsigned int no) {
	for (int i = 0; i < 10; i++) {
		mu.lock();  //上锁了,这样就算切换了线程,发现上锁了也不能继续往下执行
		sum++;
		std::cout << "我是线程" << no << ": sum 为" << sum << std::endl;
		mu.unlock(); //执行完后解锁,这样其他线程就可以执行了
	}
}

这个很好理解,因为有一个线程进入了临界区,所以其他线程必须等待临界区的访问结束后才能进入,所以程序能够正常打印sum为20

当然如果临界区里面出现了异常,导致没有正常解锁,是不是就会影响到其他线程进入临界区呢?所以我们可以使用lock_guard来解决问题。

std::default_random_engine e; //用来生成随机数
/*
线程睡眠函数
*/
void wait(int seconds) {
	boost::this_thread::sleep(boost::posix_time::seconds(seconds));
}

void f_lock_guard() {
	int i;
	for (i = 0; i < 10; i++) {
		boost::lock_guard<boost::mutex> lock(mu); 
		wait(e() % 2); //这个是一个sleep函数,e()就可以生成一个随机数了。
		sum++;
		std::cout << "我是线程" << boost::this_thread::get_id() << ": sum 为" << sum << std::endl; //这个get_id可以打印线程号,不需要传入参数来区分线程了。	
	}
	std::cout << "我是线程" << boost::this_thread::get_id() << ",我结束了。i :" << i << std::endl; 
}

int main()
{
	std::cout << boost::thread::hardware_concurrency() << std::endl;//这个可以用来显示CPU内核数,就是可以并行的线程数量
	std::cout << "程序开始" << std::endl;
	boost::thread a(f_lock_guard); //这里改动了,不再需要参数了
	boost::thread b(f_lock_guard);
	a.join(); //等待线程结束,如果没有这个的话主线程一下就结束了,子线程也没了
	b.join();
	return 0;
}

lock_guard 这个类会在构造函数里调用mu.lock(),析构函数里调用mu.unlock(),这样的话lock就会在离开作用域的时候调用析构函数,从而调用mu.unlock(),退出临界区的占用。

当程序存在多个线程需要读取资源,而只有一个线程会修改资源(即读者-写者问题),使用上面的方法并不能很好地实现。

先来描述一下要求:
(1)可以存在多个线程同时读取资源;
(2)读取资源的时候不可以修改资源;
(3)修改资源的时候不可以读取资源;

为了满足上面3个要求,要增加新类型的锁。
这里f_shared_fillrandom_numbers添加随机数,相当于写着;
f_shared_printf_shared_count 打印随机数和统计随机数,相当于读者。

boost::shared_mutex shared_mu; 
vector<int> random_numbers;

int sum = 0;
void f_shared_fill() {
	for (int i = 0; i < 5; ++i) {
		
		boost::unique_lock<boost::shared_mutex> lock(shared_mu); //写锁
		std::cout << "我要写入了" << std::endl;
		random_numbers.push_back(e()%100);
		std::cout << "我写完了" << std::endl;
		lock.unlock();
		wait(1);
	}
}

void f_shared_print() {
	for (int i = 0; i < 5; ++i) {
		wait(1);
		boost::shared_lock<boost::shared_mutex> lock(shared_mu); //读锁
		boost::this_thread::yield(); //放弃时间片,让其他线程执行
		std::cout << "我在打印" << std::endl;
		std::cout << random_numbers.back() << std::endl;
		std::cout << "我打印完了" << std::endl;
	}
}

void f_shared_count() {
	for (int i = 0; i < 5; ++i) {
		wait(1);
		boost::shared_lock<boost::shared_mutex> lock(shared_mu); //读锁
		boost::this_thread::yield(); //放弃时间片,让其他线程执行
		std::cout << "我在统计" << std::endl;
		sum += random_numbers.back();
		std::cout << "我统计完了" << std::endl;
	}
}

int main()
{
	std::cout << "程序开始" << std::endl;
	boost::thread a(f_shared_fill);
	boost::thread b(f_shared_print);
	boost::thread c(f_shared_count);
	a.join();
	b.join();
	c.join();
	std::cout << sum << std::endl;
	return 0;
}

注意写锁和读锁的区别。

boost::unique_lockboot::lock_guard类似,也是能够在构造函数中锁定,析构函数中解锁,但比boot::lock_guard更复杂。

f_shared_fill函数执行的时候上锁,保证其他线程无法访问。并且在后面有个主动调用lock.unlock的主动解锁,然后进入睡眠,保证读者线程能够执行。

f_shared_printf_shared_count开头都有一个wait,用来保证f_shared_fill比他们先执行完。然后有一个释放时间片yield,通过这个能够更好地看出在f_shared_printf_shared_count执行的过程中,cpu能够执行另外的读者线程,之所以不执行f_shared_fill是因为执行yield之前已经加了读锁。

程序是通过wait来控制线程的执行顺序,不会出现读线程执行了2次或以上而写线程未执行或是写线程写入了2次或以上而读线程一次都未执行。

当然可以用更好的方法来解决这个问题,那就是条件变量

void f_condition_fill() {
	for (int i = 0; i < 10; ++i) {
		std::cout << "我是线程1  "<<random_numbers.size()<<std::endl;
		boost::unique_lock<boost::mutex> lock(mu);
		random_numbers.push_back(e()%100);
		cond.notify_all();
		cond.wait(mu);
	}
}

void f_condition_print() {
	static int next_size = 1;
	for (int i = 0; i < 10; ++i) {
		std::cout << "我是线程2  " << random_numbers.size() << std::endl;
		boost::unique_lock<boost::mutex> lock(mu);	
		while (next_size != random_numbers.size()) {
			cond.wait(mu);
		}		
		std::cout << random_numbers.back() << std::endl;
		++next_size;
		cond.notify_all();
	}
}
int main()
{
	std::cout << "程序开始" << std::endl;
	boost::thread a(f_condition_fill);
	boost::thread b(f_condition_print);
	a.join();
	b.join();
	std::cout << sum << std::endl;
	return 0;
}

注意到这里用到的互斥量是mutex而不是shared_lock

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值