ACE 互斥体如何工作以及为什么 ACE 线程互斥体的工作方式不同?
How do ACE mutexes work and why does the ACE thread mutex work differently?
这个问题是关于ACE互斥锁的使用的。下面是一个相当冗长的最小示例,其工作方式如下:
代码启动多个线程并在 10 秒后停止它们。每个线程都尝试获取一个互斥锁并保持它直到它终止。有两种线程。第一种使用全局变量,而第二种使用局部变量。所有互斥量都具有相同的名称。由于线程共享一个进程,我希望一个线程获取互斥锁,而其他线程必须等到该线程终止。
观察:虽然全局互斥总是按预期工作,但如果用作局部变量,ACE_Thread_Mutex 和 ACE_Recursive_Thread_Mutex 会失败。尽管共享名称,但本地对象似乎是不同的互斥体。这是为什么?是否有一个类似于进程互斥体的线程互斥体。我应该提到我在 Windows.
上使用 ACE
我的第二个问题是互斥锁应该是对象还是指向对象的指针。行为有什么不同吗?当然指针可以共享,而对象更容易处理。因此我更喜欢对象,但是线程互斥似乎不像进程互斥那样工作。
非常感谢您的一些见解。
PS:我知道我总是可以使用带有前置进程 ID 或类似内容的进程互斥来模拟线程互斥。这不是我的问题。
#include <iostream>
#include <sstream>
#include <ace/Process_Mutex.h>
#include <ace/Task.h>
#include <ace/Semaphore.h>
#define LOG(msg) std::cout << msg << std::endl;
#define NAME_MUTEX_THREAD "MY_UNIQUE_MUTEX"
// switch between different mutex types
// #define MUTEX_DECL(var, name) ACE_Thread_Mutex var(name);
// #define MUTEX_DECL(var, name) ACE_Recursive_Thread_Mutex var(name);
#define MUTEX_DECL(var, name) ACE_Process_Mutex var(name);
// #define MUTEX_DECL(var, name) ACE_Semaphore var(1, USYNC_THREAD, name);
// #define MUTEX_DECL(var, name) ACE_Semaphore var(1, USYNC_PROCESS, name);
ACE_Thread_Manager * g_pThreadManager;
MUTEX_DECL(g_oMutex, NAME_MUTEX_THREAD);
using namespace std;
std::string getThreadLogPrefix(void)
{
ACE_thread_t thisThread = ACE_OS::thr_self();
const int iProcessID = ACE_OS::getpid();
const int iThreadID = thisThread;
ostringstream convert;
convert <<"P"<<iProcessID<<"T"<<iThreadID;
return convert.str();
}
ACE_THR_FUNC_RETURN threadLocalMutexRace(void * par)
{
const ACE_thread_t thisThread = ACE_OS::thr_self();
const string strLog = getThreadLogPrefix() + "_threadLocalMutexRace: ";
MUTEX_DECL(oMutex, NAME_MUTEX_THREAD);
LOG(strLog<<"Start");
LOG(strLog<<"Try to acquire mutex");
int irc = oMutex.acquire();
if (irc == -1) {
LOG(strLog<<"Mutex not acquired (code "<<irc<<").");
return 0;
}
LOG(strLog<<"Mutex acquired (code "<<irc<<").");
while(ACE_Thread_Manager::instance()->testcancel(thisThread) == 0){
;
}
LOG(strLog<<"Stop");
oMutex.release();
LOG(strLog<<"Mutex released.");
return 0;
}
ACE_THR_FUNC_RETURN threadMutexRace(void * par)
{
const ACE_thread_t thisThread = ACE_OS::thr_self();
const string strLog = getThreadLogPrefix() + "_threadMutexRace: ";
LOG(strLog<<"Start");
LOG(strLog<<"Try to acquire mutex");
int irc = g_oMutex.acquire();
if (irc == -1) {
LOG(strLog<<"Mutex not acquired (code "<<irc<<").");
return 0;
}
LOG(strLog<<"Mutex acquired (code "<<irc<<").");
while(ACE_Thread_Manager::instance()->testcancel(thisThread) == 0){
;
}
LOG(strLog<<"Stop");
g_oMutex.release();
LOG(strLog<<"Mutex released.");
return 0;
}
int main(int argc, char * argv[])
{
ACE::init();
g_pThreadManager = new ACE_Thread_Manager();
const unsigned int uiNumThreadMutexRace = 3;
if (g_pThreadManager)
{
LOG("*******************************************************************");
LOG("Start threads...");
for (unsigned int i = 0; i < uiNumThreadMutexRace; ++i)
{
g_pThreadManager->spawn((ACE_THR_FUNC)threadMutexRace, nullptr);
}
for (unsigned int i = 0; i < uiNumThreadMutexRace; ++i)
{
g_pThreadManager->spawn((ACE_THR_FUNC)threadLocalMutexRace, nullptr);
}
ACE_OS::sleep(10);
LOG("Stop threads...");
g_pThreadManager->cancel_all();
g_pThreadManager->wait();
LOG("All threads stopped.");
LOG("*******************************************************************");
}
delete g_pThreadManager;
g_pThreadManager = nullptr;
return 0;
}
该名称不是 ACE 线程互斥体的唯一标识符。您必须确保两个线程都使用相同的 ACE 线程互斥锁实例,通过使用局部变量,两个线程都有自己的实例,它们都可以独立锁定。通常在 C++ 中,您将互斥锁作为 class 成员,以便您可以在 class 的各种操作中使用它。我建议阅读 C++ Network Programming volume 1 book.
的第 10 章
有几件事要记住。
1) 每个线程都有自己的堆栈,这意味着每个线程都有自己的变量副本,包括互斥锁。
2) 互斥量只有在线程可以访问相同的内存地址时才能工作。
您正在为每个线程创建一个锁,而多个线程之间应该有一个锁。
这个问题是关于ACE互斥锁的使用的。下面是一个相当冗长的最小示例,其工作方式如下:
代码启动多个线程并在 10 秒后停止它们。每个线程都尝试获取一个互斥锁并保持它直到它终止。有两种线程。第一种使用全局变量,而第二种使用局部变量。所有互斥量都具有相同的名称。由于线程共享一个进程,我希望一个线程获取互斥锁,而其他线程必须等到该线程终止。
观察:虽然全局互斥总是按预期工作,但如果用作局部变量,ACE_Thread_Mutex 和 ACE_Recursive_Thread_Mutex 会失败。尽管共享名称,但本地对象似乎是不同的互斥体。这是为什么?是否有一个类似于进程互斥体的线程互斥体。我应该提到我在 Windows.
上使用 ACE我的第二个问题是互斥锁应该是对象还是指向对象的指针。行为有什么不同吗?当然指针可以共享,而对象更容易处理。因此我更喜欢对象,但是线程互斥似乎不像进程互斥那样工作。
非常感谢您的一些见解。
PS:我知道我总是可以使用带有前置进程 ID 或类似内容的进程互斥来模拟线程互斥。这不是我的问题。
#include <iostream>
#include <sstream>
#include <ace/Process_Mutex.h>
#include <ace/Task.h>
#include <ace/Semaphore.h>
#define LOG(msg) std::cout << msg << std::endl;
#define NAME_MUTEX_THREAD "MY_UNIQUE_MUTEX"
// switch between different mutex types
// #define MUTEX_DECL(var, name) ACE_Thread_Mutex var(name);
// #define MUTEX_DECL(var, name) ACE_Recursive_Thread_Mutex var(name);
#define MUTEX_DECL(var, name) ACE_Process_Mutex var(name);
// #define MUTEX_DECL(var, name) ACE_Semaphore var(1, USYNC_THREAD, name);
// #define MUTEX_DECL(var, name) ACE_Semaphore var(1, USYNC_PROCESS, name);
ACE_Thread_Manager * g_pThreadManager;
MUTEX_DECL(g_oMutex, NAME_MUTEX_THREAD);
using namespace std;
std::string getThreadLogPrefix(void)
{
ACE_thread_t thisThread = ACE_OS::thr_self();
const int iProcessID = ACE_OS::getpid();
const int iThreadID = thisThread;
ostringstream convert;
convert <<"P"<<iProcessID<<"T"<<iThreadID;
return convert.str();
}
ACE_THR_FUNC_RETURN threadLocalMutexRace(void * par)
{
const ACE_thread_t thisThread = ACE_OS::thr_self();
const string strLog = getThreadLogPrefix() + "_threadLocalMutexRace: ";
MUTEX_DECL(oMutex, NAME_MUTEX_THREAD);
LOG(strLog<<"Start");
LOG(strLog<<"Try to acquire mutex");
int irc = oMutex.acquire();
if (irc == -1) {
LOG(strLog<<"Mutex not acquired (code "<<irc<<").");
return 0;
}
LOG(strLog<<"Mutex acquired (code "<<irc<<").");
while(ACE_Thread_Manager::instance()->testcancel(thisThread) == 0){
;
}
LOG(strLog<<"Stop");
oMutex.release();
LOG(strLog<<"Mutex released.");
return 0;
}
ACE_THR_FUNC_RETURN threadMutexRace(void * par)
{
const ACE_thread_t thisThread = ACE_OS::thr_self();
const string strLog = getThreadLogPrefix() + "_threadMutexRace: ";
LOG(strLog<<"Start");
LOG(strLog<<"Try to acquire mutex");
int irc = g_oMutex.acquire();
if (irc == -1) {
LOG(strLog<<"Mutex not acquired (code "<<irc<<").");
return 0;
}
LOG(strLog<<"Mutex acquired (code "<<irc<<").");
while(ACE_Thread_Manager::instance()->testcancel(thisThread) == 0){
;
}
LOG(strLog<<"Stop");
g_oMutex.release();
LOG(strLog<<"Mutex released.");
return 0;
}
int main(int argc, char * argv[])
{
ACE::init();
g_pThreadManager = new ACE_Thread_Manager();
const unsigned int uiNumThreadMutexRace = 3;
if (g_pThreadManager)
{
LOG("*******************************************************************");
LOG("Start threads...");
for (unsigned int i = 0; i < uiNumThreadMutexRace; ++i)
{
g_pThreadManager->spawn((ACE_THR_FUNC)threadMutexRace, nullptr);
}
for (unsigned int i = 0; i < uiNumThreadMutexRace; ++i)
{
g_pThreadManager->spawn((ACE_THR_FUNC)threadLocalMutexRace, nullptr);
}
ACE_OS::sleep(10);
LOG("Stop threads...");
g_pThreadManager->cancel_all();
g_pThreadManager->wait();
LOG("All threads stopped.");
LOG("*******************************************************************");
}
delete g_pThreadManager;
g_pThreadManager = nullptr;
return 0;
}
该名称不是 ACE 线程互斥体的唯一标识符。您必须确保两个线程都使用相同的 ACE 线程互斥锁实例,通过使用局部变量,两个线程都有自己的实例,它们都可以独立锁定。通常在 C++ 中,您将互斥锁作为 class 成员,以便您可以在 class 的各种操作中使用它。我建议阅读 C++ Network Programming volume 1 book.
的第 10 章有几件事要记住。
1) 每个线程都有自己的堆栈,这意味着每个线程都有自己的变量副本,包括互斥锁。
2) 互斥量只有在线程可以访问相同的内存地址时才能工作。
您正在为每个线程创建一个锁,而多个线程之间应该有一个锁。