编程语言
首页 > 编程语言> > c++多线程

c++多线程

作者:互联网

参考链接:https://www.cnblogs.com/zizbee/p/13520823.html

c++创建线程的方式

需要包含头文件#include <thread>

// 准备用于创建线程的函数
void proc(int a) {
	std::cout << "我是子线程" << std::this_thread::get_id() << ",传入参数为" << a << std::endl;
	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	std::cout << "子线程" << std::this_thread::get_id() << "结束" << std::endl;
}

int main(){
	std::cout << "我是主线程" << std::endl;
    thread th(proc,9); // 创建线程
    th.join(); // 主线程阻塞的,等待th线程执行结束主线程再继续
	std::cout << "主线程" << std::this_thread::get_id() << "结束" << std::endl;
	return 0;
}

// 注意:只要创建了线程对象(传递“函数名/可调用对象”作为参数的情况下),线程就开始执行(std::thread 有一个无参构造函数重载的版本,不会创建底层的线程)。

多线程经典案例:买票

同时开启多个线程进行购票,如果不加锁就会出现以下情况:

代码:

#include <thread>
#include <iostream>
#include <vector>

int ticket_num_left = 10; // 一共10张票

void buy_ticket(int id) {
	while (true) {
		if (ticket_num_left > 0) {
			std::this_thread::sleep_for(std::chrono::milliseconds(30));
			ticket_num_left--;
			std::cout << "线程" << id << "买了一张票,现在还剩" << ticket_num_left << "张票" << std::endl;
		}
		else {
			std::cout << "线程" << id << "无票可买" << std::endl;
			break;
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(30));
	}
}

int main() {
	std::cout << "我是主线程" << std::endl;
	std::vector<std::thread*> threads;

    // 同时开启9个线程一起买票
	for (int i = 0; i < 9; i++) {
		std::thread* th_ptr = new std::thread(buy_ticket, i + 1);
		threads.push_back(th_ptr);
	}

	for (std::vector<std::thread*>::iterator it = threads.begin(); it != threads.end(); it++) {
		(*it)->join();
	}

	std::cout << "主线程" << std::this_thread::get_id() << "结束" << std::endl;
	return 0;
}

运行结果如下,出现奇怪的票数:

我是主线程
线程5买了一张票,现在还剩2张票
线程2买了一张票,现在还剩2张票
线程9买了一张票,现在还剩2张票
线程8买了一张票,现在还剩2张票
线程1买了一张票,现在还剩2张票
线程3买了一张票,现在还剩2张票
线程7买了一张票,现在还剩2张票
线程6买了一张票,现在还剩2张票
线程4买了一张票,现在还剩2张票
线程2买了一张票,现在还剩-1张票
线程9买了一张票,现在还剩-1张票
线程5买了一张票,现在还剩-1张票
线程7买了一张票,现在还剩-7张票
线程1买了一张票,现在还剩-7张票
线程4买了一张票,现在还剩-7张票
线程3买了一张票,现在还剩-7张票
线程8买了一张票,现在还剩-7张票
线程6买了一张票,现在还剩-7张票
线程2无票可买
线程5无票可买
线程9无票可买
线程3无票可买
线程6无票可买
线程8无票可买
线程7无票可买
线程1无票可买
线程4无票可买
主线程23060结束

原因分析:

解决方法:单个线程在购票时,使用互斥量加锁

#include <mutex>

方法一:lock()与unlock()

代码:

#include <thread>
#include <iostream>
#include <vector>
#include <mutex>

int ticket_num_left = 10;
std::mutex mutex_;

void buy_ticket(int id) {
	while (true) {
		mutex_.lock();
		if (ticket_num_left > 0) {
			std::this_thread::sleep_for(std::chrono::milliseconds(30));
			ticket_num_left--;
			std::cout << "线程" << id << "买了一张票,现在还剩" << ticket_num_left << "张票" << std::endl;
		}
		else {
			std::cout << "线程" << id << "无票可买" << std::endl;
			mutex_.unlock();
			break;
		}
		mutex_.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(30));
	}
}

int main() {
	std::cout << "我是主线程" << std::endl;
	std::vector<std::thread*> threads;

	for (int i = 0; i < 9; i++) {
		std::thread* th_ptr = new std::thread(buy_ticket, i + 1);
		threads.push_back(th_ptr);
	}

	for (std::vector<std::thread*>::iterator it = threads.begin(); it != threads.end(); it++) {
		(*it)->join();
	}

	std::cout << "主线程" << std::this_thread::get_id() << "结束" << std::endl;
	return 0;
}

运行结果:

我是主线程
线程2买了一张票,现在还剩9张票
线程1买了一张票,现在还剩8张票
线程3买了一张票,现在还剩7张票
线程4买了一张票,现在还剩6张票
线程5买了一张票,现在还剩5张票
线程6买了一张票,现在还剩4张票
线程7买了一张票,现在还剩3张票
线程8买了一张票,现在还剩2张票
线程9买了一张票,现在还剩1张票
线程2买了一张票,现在还剩0张票
线程1无票可买
线程3无票可买
线程4无票可买
线程5无票可买
线程6无票可买
线程7无票可买
线程8无票可买
线程9无票可买
线程2无票可买
主线程17224结束

方法二:lock_guard()

创建即加锁,作用域结束自动解锁。

代码:

#include <thread>
#include <iostream>
#include <vector>
#include <mutex>

int ticket_num_left = 10;
std::mutex mutex_;

void buy_ticket(int id) {
	while (true) {
		std::lock_guard<std::mutex> lockGuard(mutex_); // 用lock_guard替代lock和unlock
		//mutex_.lock();
		if (ticket_num_left > 0) {
			std::this_thread::sleep_for(std::chrono::milliseconds(30));
			ticket_num_left--;
			std::cout << "线程" << id << "买了一张票,现在还剩" << ticket_num_left << "张票" << std::endl;
		}
		else {
			std::cout << "线程" << id << "无票可买" << std::endl;
			//mutex_.unlock();
			break;
		}
		//mutex_.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(30));
	}
}

int main() {
	std::cout << "我是主线程" << std::endl;
	std::vector<std::thread*> threads;

	for (int i = 0; i < 9; i++) {
		std::thread* th_ptr = new std::thread(buy_ticket, i + 1);
		threads.push_back(th_ptr);
	}

	for (std::vector<std::thread*>::iterator it = threads.begin(); it != threads.end(); it++) {
		(*it)->join();
	}

	std::cout << "主线程" << std::this_thread::get_id() << "结束" << std::endl;
	return 0;
}

方法三:unique_lock

unique_lock类似于lock_guard,只是unique_lock用法更加丰富,同时支持lock_guard()的原有功能。
使用lock_guard后不能手动lock()与手动unlock();使用unique_lock后可以手动lock()与手动unlock();
unique_lock的第二个参数,除了可以是adopt_lock,还可以是try_to_lock与defer_lock;
try_to_lock: 尝试去锁定,得保证锁处于unlock的状态,然后尝试现在能不能获得锁;尝试用mutx的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,不会阻塞在那里
defer_lock: 始化了一个没有加锁的mutex;

lock_guard unique_lock
手动lock与手动unlock 不支持 支持
参数 支持adopt_lock 支持adopt_lock/try_to_lock/defer_lock

详见链接:https://www.cnblogs.com/zizbee/p/13520823.html

void buy_ticket(int id) {
	while (true) {
		std::unique_lock<std::mutex> deferLock(mutex_, std::defer_lock);//始化了一个没有加锁的mutex
		//std::lock_guard<std::mutex> lockGuard(mutex_);
		//mutex_.lock();
		deferLock.lock();
		if (ticket_num_left > 0) {
			std::this_thread::sleep_for(std::chrono::milliseconds(30));
			ticket_num_left--;
			std::cout << "线程" << id << "买了一张票,现在还剩" << ticket_num_left << "张票" << std::endl;
		}
		else {
			std::cout << "线程" << id << "无票可买" << std::endl;
			//mutex_.unlock();
			deferLock.unlock();
			break;
		}
		//mutex_.unlock();
		deferLock.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(30));
	}
}

picLoadErr

标签:std,lock,张票,c++,线程,ticket,多线程,无票
来源: https://www.cnblogs.com/shegb1997/p/16255295.html