多线程——线程互斥锁

1.前言

多线程技术是一种使计算机能够同时执行多个任务的技术。在软件开发中,多线程可以提高应用程序的响应性和计算密集型任务的性能。

多应用于Web服务器和专用服务器后台任务和异步处理分布式计算实时交互系统
以下是我整理的我这几天学习到的多线程的知识。

以下代码所需要的头文件

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

2.概念

为甚莫要使用互斥锁呢,因为在多线程编程中,可能出现许多线程要使用**同一变量**,但是每个线程对这个变量的**读操作**和**写操作**是需要时间的。一个线程可能读取到另一个线程**写操作**完成之前的数据,并且和另一个线程执行了同样的操作。

比如说有两个用户同时修改一段数据(读取到了同一段数据),他们同时在结尾加了一段数据(同时进行写操作),后执行完写操作的人的数据覆盖了先执行完写操作的数据,导致其中一人的数据丢失。

而想要避免上面这种情况发生,必须在对共享的数据进行写操作的时候只能由一个线程来写,等到这个线程的写操作完成后,才能轮到其他线程来执行写操作。这就要使用互斥锁

互斥锁的作用,是使一段程序变成临界区

在多线程编程中,临界区(Critical Section)是指每个线程中访问临界资源的那段代码。临界资源是一次仅允许一个线程使用的共享资源。临界区的主要目的是确保多个线程互斥地访问共享资源,从而避免数据竞争和不一致性。

临界资源也就是上面说的共享的数据(由多个线程去访问)。

3.代码

1.互斥锁

下面的代码展示的互斥锁std::mutex的使用方法。
int count = 0;	//共享变量
std::mutex mtx;	//互斥锁
void thread_func(int times) {
	for (int i = 0; i < times; i++) {
		//竞争
		mtx.lock();	//上锁
		count++;
		mtx.unlock();	//解锁
	}
}

这里使用了c++的mutex互斥锁。当一个线程使用lock()函数上锁后,别的线程将无法上锁,只能等到上锁的线程使用unlock()函数解锁以后才能上锁。

mutex有以下几个函数

  • 构造函数,mutex不能使用拷贝构造函数和移动构造函数。
    mutex(const mutex&)            = delete;
    mutex& operator=(const mutex&) = delete;
  • lock()函数,加锁。如果别的线程已经加锁,线程会在此处阻塞。
    注意!!!如果对同一个线程重复加锁可能导致死锁。
  • unlock()函数,解锁。
  • try_lock()函数,尝试锁住互斥量。和lock()不同的是如果别的线程已经加锁,则不会阻塞而是继续执行下去。

以下是主函数

int main() {
	int times = 1000000;
	std::thread t1(thread_func, times);
	std::thread t2(thread_func, times);
	t1.join();
	t2.join();
	std::cout << "count:" << count << std::endl;
	return 0;
}

得出的结果是

count:2000000

2.定时互斥锁

还有另一个会用到的互斥锁是std::timed_mutex.(定时互斥锁)std::timed_mutex比mutex多了两个函数,分别为
  • try_lock_for()函数,传入一个时间范围,在这时间范围没有获得锁则会阻塞住。超出时间范围则会返回false。
std::timed_mutex tmtx;	//定时互斥量
void thread_func2(int times) {
	for (int i = 0; i < times; i++) {
		//竞争
		if (tmtx.try_lock_for(std::chrono::milliseconds(200))) {	//上锁,尝试在200ms内获取锁
			std::cout << "thread_func2:" << std::this_thread::get_id() << std::endl;
			tmtx.unlock();	//解锁
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1秒
	}
}

上述代码演示了try_lock_for()的使用方法。线程获得锁后打印线程的id。

其中std::chrono::milliseconds(200)使用了库chrono来表示 500 毫秒的时间间隔。

  • try_lock_until()函数,传入一个时间点,在这时间点到来之前没有获得锁则会阻塞住。超出时间点则会返回false。
void thread_func2(int times) {
	for (int i = 0; i < times; i++) {
		//竞争
		auto now = std::chrono::system_clock::now();	//获取当前时间
		if (tmtx.try_lock_until(now + std::chrono::milliseconds(200))) {	//上锁
			std::cout << "thread_func2:" << std::this_thread::get_id() << std::endl;
			tmtx.unlock();	//解锁
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1秒
	}
}

上述代码演示了try_lock_until()的使用方法。线程获得锁后打印线程的id。

其中auto now = std::chrono::system_clock::now();使用库chrono来获取当前时间。

而now + std::chrono::milliseconds(200)表示在当前时间200ms后的时间点。

以上为我在学习过程中整理的知识点,如有哪里说错了感谢指出
推荐一个讲的比较好的up主的视频【32.线程同步与mutex】https://www.bilibili.com/video/BV1eZ421g7jK?vd_source=dccc0abff62c8559f0a5ed0bce39dec2

posted @ 2025-04-08 23:29  散尽天华  阅读(108)  评论(0)    收藏  举报