【发布时间】:2022-01-09 23:10:33
【问题描述】:
我正在尝试管理嵌入式系统中相机的优先资源访问。因此,我制作了一个相机接口,它使用 std::condition_variable 来实现这一点。
typedef std::function<void()> UnlockCameraHandler;
class Camera {
const int MAX_PRIORITY = std::numeric_limits<int>::max();
int currentPriority = MAX_PRIORITY;
bool mLockable = true;
UnlockCameraHandler mUnlockCameraHandler;
std::condition_variable_any cameraLock;
std::mutex cameraMut;
public:
bool isLockable(int priority) {
std::unique_lock<std::mutex> mLock(cameraMut);
if (priority < currentPriority) {
mLock.unlock();
return true;
} else {
mLock.unlock();
return false;
}
}
void unlock() {
std::unique_lock<std::mutex> mLock(cameraMut);
if (mLockable) {
return;
}
currentPriority = MAX_PRIORITY;
mLockable = true;
cameraLock.notify_all();
mLock.unlock();
}
bool lock(int priority, UnlockCameraHandler handler) {
if (priority < 0) {
return false;
}
std::unique_lock<std::mutex> mLock(cameraMut);
if (priority < currentPriority) {
if (mUnlockCameraHandler) {
mUnlockCameraHandler();
}
}
if (!mLockable) {
cameraLock.wait(mLock, std::bind(&Camera::isLockable, this, priority));
}
mUnlockCameraHandler = handler;
currentPriority = priority;
mLockable = false;
mLock.unlock();
return true;
}
};
我为这些函数编写了一些 Catch2 单元测试,以确保它可以与多个线程一起使用。
struct CameraLock {
std::shared_ptr<TestCamera> camera;
void unlock_handler() { camera->unlock(); }
};
TEST_CASE_METHOD(CameraLock, "Camera lock mechanism") {
SECTION("lock and unlock camera") {
camera->lock(100, std::bind(&CameraLock::unlock_handler, this));
REQUIRE(!camera->isLockable(100));
camera->unlock();
REQUIRE(camera->isLockable(100));
}
SECTION("lock with higher priority") {
std::thread low{[&]() {
REQUIRE(camera->lock(100, std::bind(&CameraLock::unlock_handler, this)));
}};
std::thread high{[&]() {
REQUIRE(camera->lock(50, std::bind(&CameraLock::unlock_handler, this)));
}};
low.join();
high.join();
}
SECTION("lock with lower priority") {
std::thread high{[&]() {
REQUIRE(camera->lock(50, std::bind(&CameraLock::unlock_handler, this)));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
camera->unlock();
}};
std::thread low{[&]() {
REQUIRE(camera->lock(100, std::bind(&CameraLock::unlock_handler, this)));
camera->unlock();
}};
high.join();
low.join();
REQUIRE(camera->isLockable(100));
}
SECTION("lock with 3 parties") {
std::thread high{[&]() {
REQUIRE(camera->lock(50, std::bind(&CameraLock::unlock_handler, this)));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
camera->unlock();
}};
std::thread low{[&]() {
REQUIRE(camera->lock(100, std::bind(&CameraLock::unlock_handler, this)));
camera->unlock();
}};
std::thread mid{[&]() {
REQUIRE(camera->lock(75, std::bind(&CameraLock::unlock_handler, this)));
std::this_thread::sleep_for(std::chrono::milliseconds(5));
camera->unlock();
}};
high.join();
low.join();
mid.join();
REQUIRE(camera->isLockable(100));
}
}
它应该以这样的方式工作,当一个进程想要访问相机资源时,它调用 lock() 来保留它,传递一个优先级值和一个回调函数。 Callback 函数的目的是,如果另一个进程想要访问具有更高优先级的摄像头,当前具有访问权限的进程会做适当的工作以释放资源并在之后调用 unlock(),以便优先级进程可以锁定它. 如果优先级较低的进程想要访问资源,它会调用 condition_variable 的 wait() 函数并阻塞,直到优先级进程调用 unlock()。
单元测试在大多数情况下都会通过,但有时我会遇到分段错误或双重 free() 错误(尤其是在“与 3 方锁定”部分),这告诉我我的实现不是线程安全。
下面是一个最小的代码示例,供所有可能想要调试它的人运行:
#include <condition_variable>
#include <mutex>
#include <thread>
#include <functional>
typedef std::function<void()> UnlockCameraHandler;
class Camera {
const int MAX_PRIORITY = std::numeric_limits<int>::max();
int currentPriority = MAX_PRIORITY;
bool mLockable = true;
UnlockCameraHandler mUnlockCameraHandler;
std::condition_variable_any cameraLock;
std::mutex cameraMut;
public:
bool isLockable(int priority) {
if (priority < currentPriority) {
mLock.unlock();
return true;
} else {
mLock.unlock();
return false;
}
}
void unlock() {
std::unique_lock<std::mutex> mLock(cameraMut);
if (mLockable) {
return;
}
currentPriority = MAX_PRIORITY;
mLockable = true;
cameraLock.notify_all();
mLock.unlock();
}
bool lock(int priority, UnlockCameraHandler handler) {
if (priority < 0) {
return false;
}
std::unique_lock<std::mutex> mLock(cameraMut);
if (priority < currentPriority) {
if (mUnlockCameraHandler) {
mUnlockCameraHandler();
}
}
if (!mLockable) {
cameraLock.wait(mLock, std::bind(&Camera::isLockable, this, priority));
}
mUnlockCameraHandler = handler;
currentPriority = priority;
mLockable = false;
mLock.unlock();
return true;
}
};
std::shared_ptr<Camera> camera = std::make_shared<Camera>();
void unlock_handler() { camera->unlock(); }
int main() {
std::thread high{[&]() {
camera->lock(50, std::bind(&unlock_handler));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
camera->unlock();
}};
std::thread low{[&]() {
camera->lock(100, std::bind(&unlock_handler));
camera->unlock();
}};
std::thread mid{[&]() {
camera->lock(75, std::bind(&unlock_handler));
std::this_thread::sleep_for(std::chrono::milliseconds(5));
camera->unlock();
}};
high.join();
low.join();
mid.join();
}
有人可以告诉我我的代码有什么问题以及如何调整它以满足我的例外情况吗? 提前致谢!
【问题讨论】:
-
只是一个您似乎错过的注释:“
wait导致当前线程阻塞,直到通知条件变量或发生虚假唤醒" -
@TedLyngmo 不应该通过将
isLockable函数传递给wait来解决这个问题吗? -
为什么 m_lockable 和优先级并不总是受互斥代码保护的一部分?
-
@Federico 如果我在重置变量之前在
unlock中获取互斥锁,测试用例有时会冻结,可能正在等待另一个线程解锁互斥锁,这永远不会发生。在这种情况下,我也看到了double free or corruption (out)错误。 -
unlock需要在修改currentPriority和mLockable之前锁定cameraMut,否则您将遇到竞争条件。lock还需要先锁定cameraMut才能测试mLockable
标签: c++ multithreading