我有一个运行多个线程的应用程序,有时会被取消。这些线程可能会调用内部访问资源(套接字)的另一个对象。为了防止同时访问资源,有一个关键部分可以在执行中获得一些顺序。
现在,当取消线程时,它(有时)会发生线程就在被关键部分阻止的代码中。临界区使用一个对象锁定,我希望在取消线程后,该对象将被破坏并因此释放锁。但是,似乎并非如此,因此在线程销毁时,此资源对象将永久锁定。
更改资源对象可能不是一个选项(第三方交付),另外有必要防止同时访问不能并行使用的资源。
我已尝试在锁定/解锁部分时使用pthread_setcancelstate阻止线程被取消,但是这确实感觉有点脏,并且不会成为其他情况的最终解决方案(例如获取的互斥锁等)。
我知道一个首选的解决方案是不使用pthread_cancel,而是在线程中设置一个标志,它会在准备就绪时以一种干净的方式取消它。然而,由于我想尽快取消线程,我想知道(也出于学术兴趣)是否还有其他选择。
答案 0 :(得分:2)
没有应用程序的帮助(提到的标志)的线程取消是一个坏主意。只需google。
实际上取消是如此困难,以至于从最新的C ++ 0x草案中省略了它。您可以搜索http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2497.html,但根本不会提及取消。这是建议的线程类的定义(你不会在那里找到取消):
class thread
{
public:
// types:
class id;
typedef implementation-defined native_handle_type; // See [thread.native]
// construct/copy/destroy:
thread();
template <class F> explicit thread(F f);
template <class F, class ...Args> thread(F&& f, Args&&... args);
~thread();
thread(const thread&) = delete;
thread(thread&&);
thread& operator=(const thread&) = delete;
thread& operator=(thread&&);
// members:
void swap(thread&&);
bool joinable() const;
void join();
void detach();
id get_id() const;
native_handle_type native_handle(); // See [thread.native]
// static members:
static unsigned hardware_concurrency();
};
答案 1 :(得分:2)
您可以使用pthread_cleanup_push()将取消清理处理程序推送到线程取消清理堆栈。该处理程序将负责解锁关键部分。
离开临界区后,您应该调用pthread_cleanup_pop(0)将其删除。
即
CRIITICAL_SECTION g_section;
void clean_crit_sec( void * )
{
LeaveCriticalSection( &g_section )
}
void *thrfunc( void * )
{
EnterCriticalSection( &g_section );
pthread_cleanup_push( clean_crit_sec, NULL );
// Do something that may be cancellable
LeaveCriticalSection( &g_section );
pthread_cleanup_pop( 0 );
}
这仍然会留下一个小的竞争状态,其中关键部分已被解锁,但如果线程在Leave ..和cleanup_pop之间被取消,则仍然可以执行清理处理程序。
您可以使用1来调用pthread_cleanup_pop,这将执行您的清理代码,而不是自己处理关键部分。即
CRIITICAL_SECTION g_section;
void clean_crit_sec( void * )
{
LeaveCriticalSection( &g_section )
}
void *thrfunc( void * )
{
EnterCriticalSection( &g_section );
pthread_cleanup_push( clean_crit_sec, NULL );
// Do something that may be cancellable
pthread_cleanup_pop( 1 ); // this will pop the handler and execute it.
}
答案 2 :(得分:0)
如果控制关键部分的锁不直接暴露给你,那么你无能为力。取消线程时,线程的所有清理处理程序都以正常的相反顺序执行,但这些处理程序当然只能释放您有权访问的互斥锁。因此,除了在访问第三方组件期间禁用取消时,你真的做不了多少。
我认为您最好的解决方案是两者使用标志和 pthread_cancel功能。当您输入第三方组件时,请禁用取消处理(PTHREAD_CANCEL_DISABLE);当你退出它时,重新启用它。重新启用后,检查标志:
/* In thread which you want to be able to be canceled: */
int oldstate;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
... call 3rd party component ...
pthread setcancelstate(oldstate, NULL);
if (cancelled_flag) pthread_exit(PTHREAD_CANCELED);
/* In the thread canceling the other one. Note the order of operations
to avoid race condition: */
cancelled_flag = true;
pthread_cancel(thread_id);
答案 3 :(得分:0)
在不使用定义明确的控制方法(即标志)的情况下中止线程的想法是如此邪恶,以至于你根本不应该这样做。
如果您有第三方代码,除了这样做之外别无选择,我可能会建议在流程中抽象出可怕的代码,然后与流程进行交互,将每个这样的组件很好地分开。 / p>
现在,这样的设计在Windows上会更糟糕,因为Windows不擅长运行多个进程,但这对linux来说并不是一个坏主意。
当然,为您的螺纹模块设计合理的设计会更好......
(就个人而言,我更喜欢根本不使用线程,并且总是使用进程或非阻塞设计)