在 C++11 中,使用 std::unique_lock<std::mutex> 作为 class 成员是否明智(甚至安全)?如果是这样,是否有任何指导方针?
In C++11, is it wise (or even safe) to use std::unique_lock<std::mutex> as a class member? If so, are there any guidelines?
使用 std::unique_lock 作为 class 成员是否明智(甚至安全)?如果是这样,有什么指导方针吗?
我使用std::unique_lock的想法是确保在抛出异常的情况下解锁互斥锁。
下面的代码给出了我当前如何使用 unique_lock 的示例。我想知道在项目增长太多之前我是否走错了方向。
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <unistd.h>
class WorkerClass {
private:
std::thread workerThread;
bool workerThreadRunning;
int workerThreadInterval;
int sharedResource;
std::mutex mutex;
std::unique_lock<std::mutex> workerMutex;
public:
WorkerClass() {
workerThreadRunning = false;
workerThreadInterval = 2;
sharedResource = 0;
workerMutex = std::unique_lock<std::mutex>(mutex);
unlockMutex();
}
~WorkerClass() {
stopWork();
}
void startWork() {
workerThreadRunning = true;
workerThread = std::thread(&WorkerClass::workerThreadMethod,
this);
}
void stopWork() {
lockMutex();
if (workerThreadRunning) {
workerThreadRunning = false;
unlockMutex();
workerThread.join();
}else {
unlockMutex();
}
}
void lockMutex() {
try {
workerMutex.lock();
}catch (std::system_error &error) {
std::cout << "Already locked" << std::endl;
}
}
void unlockMutex() {
try {
workerMutex.unlock();
}catch (std::system_error &error) {
std::cout << "Already unlocked" << std::endl;
}
}
int getSharedResource() {
int result;
lockMutex();
result = sharedResource;
unlockMutex();
return result;
}
void workerThreadMethod() {
bool isRunning = true;
while (isRunning) {
lockMutex();
sharedResource++;
std::cout << "WorkerThread: sharedResource = "
<< sharedResource << std::endl;
isRunning = workerThreadRunning;
unlockMutex();
sleep(workerThreadInterval);
}
}
};
int main(int argc, char *argv[]) {
int sharedResource;
WorkerClass *worker = new WorkerClass();
std::cout << "ThisThread: Starting work..." << std::endl;
worker->startWork();
for (int i = 0; i < 10; i++) {
sleep(1);
sharedResource = worker->getSharedResource();
std::cout << "ThisThread: sharedResource = "
<< sharedResource << std::endl;
}
worker->stopWork();
std::cout << "Done..." << std::endl;
return 0;
}
这其实很糟糕。将 std::unique_lock
或 std::lock_guard
存储为成员变量错过了作用域锁定和一般锁定的要点。
想法是在线程之间拥有共享锁,但每个临时锁定锁保护的共享资源。包装器对象使其 return-from-function 安全且异常安全。
您首先应该考虑您的共享资源。在 "Worker" 的上下文中,我会想象一些任务队列。然后,该任务队列与某个锁相关联。每个工作人员使用 scoped-wrapper 锁定该锁以对任务进行排队或出队。只要工作线程的某个实例还活着,就没有真正的理由保持锁锁定,它应该在需要时锁定它。
出于多种原因,这样做并不是一个好主意。首先,您已经 "handling" 使用 try-catch 块:两个线程试图锁定同一个锁会导致异常。如果您想要非阻塞锁定尝试,您应该改用 try_lock
。
第二个原因是,当std::unique_lock
在锁的持续时间范围内进行堆栈分配时,当它被破坏时,它会为你解锁资源。这意味着它是异常安全的,如果 workerThread.join()
抛出您当前的代码,那么锁将保持获取状态。
使用 std::unique_lock 作为 class 成员是否明智(甚至安全)?如果是这样,有什么指导方针吗?
我使用std::unique_lock的想法是确保在抛出异常的情况下解锁互斥锁。
下面的代码给出了我当前如何使用 unique_lock 的示例。我想知道在项目增长太多之前我是否走错了方向。
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <unistd.h>
class WorkerClass {
private:
std::thread workerThread;
bool workerThreadRunning;
int workerThreadInterval;
int sharedResource;
std::mutex mutex;
std::unique_lock<std::mutex> workerMutex;
public:
WorkerClass() {
workerThreadRunning = false;
workerThreadInterval = 2;
sharedResource = 0;
workerMutex = std::unique_lock<std::mutex>(mutex);
unlockMutex();
}
~WorkerClass() {
stopWork();
}
void startWork() {
workerThreadRunning = true;
workerThread = std::thread(&WorkerClass::workerThreadMethod,
this);
}
void stopWork() {
lockMutex();
if (workerThreadRunning) {
workerThreadRunning = false;
unlockMutex();
workerThread.join();
}else {
unlockMutex();
}
}
void lockMutex() {
try {
workerMutex.lock();
}catch (std::system_error &error) {
std::cout << "Already locked" << std::endl;
}
}
void unlockMutex() {
try {
workerMutex.unlock();
}catch (std::system_error &error) {
std::cout << "Already unlocked" << std::endl;
}
}
int getSharedResource() {
int result;
lockMutex();
result = sharedResource;
unlockMutex();
return result;
}
void workerThreadMethod() {
bool isRunning = true;
while (isRunning) {
lockMutex();
sharedResource++;
std::cout << "WorkerThread: sharedResource = "
<< sharedResource << std::endl;
isRunning = workerThreadRunning;
unlockMutex();
sleep(workerThreadInterval);
}
}
};
int main(int argc, char *argv[]) {
int sharedResource;
WorkerClass *worker = new WorkerClass();
std::cout << "ThisThread: Starting work..." << std::endl;
worker->startWork();
for (int i = 0; i < 10; i++) {
sleep(1);
sharedResource = worker->getSharedResource();
std::cout << "ThisThread: sharedResource = "
<< sharedResource << std::endl;
}
worker->stopWork();
std::cout << "Done..." << std::endl;
return 0;
}
这其实很糟糕。将 std::unique_lock
或 std::lock_guard
存储为成员变量错过了作用域锁定和一般锁定的要点。
想法是在线程之间拥有共享锁,但每个临时锁定锁保护的共享资源。包装器对象使其 return-from-function 安全且异常安全。
您首先应该考虑您的共享资源。在 "Worker" 的上下文中,我会想象一些任务队列。然后,该任务队列与某个锁相关联。每个工作人员使用 scoped-wrapper 锁定该锁以对任务进行排队或出队。只要工作线程的某个实例还活着,就没有真正的理由保持锁锁定,它应该在需要时锁定它。
出于多种原因,这样做并不是一个好主意。首先,您已经 "handling" 使用 try-catch 块:两个线程试图锁定同一个锁会导致异常。如果您想要非阻塞锁定尝试,您应该改用 try_lock
。
第二个原因是,当std::unique_lock
在锁的持续时间范围内进行堆栈分配时,当它被破坏时,它会为你解锁资源。这意味着它是异常安全的,如果 workerThread.join()
抛出您当前的代码,那么锁将保持获取状态。