4

我有一个运行多个线程的应用程序,这些线程有时会被取消。这些线程可能会调用另一个内部访问资源(套接字)的对象。为了防止同时访问资源,有一个关键部分可以在执行中获得一些顺序。

现在,当取消线程时,(有时)会发生线程正好在被临界区阻塞的代码中。临界区使用对象锁定,我希望在取消线程后,该对象将被破坏并因此释放锁定。然而,情况似乎并非如此,因此在线程销毁时,该资源对象被永久锁定。

更改资源对象可能不是一种选择(第 3 方交付),此外,防止同时访问无法并行使用的资源是有意义的。

我已经尝试过在该部分被锁定/解锁时使用 pthread_setcancelstate 来防止线程被取消,但是这确实感觉有点脏,并且不会成为其他情况的最终解决方案(例如获取互斥锁等)。

我知道首选的解决方案是不使用 pthread_cancel 而是在线程中设置一个标志,它会在准备好时自行取消(以干净的方式)。但是,由于我想尽快取消线程,我想知道(也是出于学术兴趣)是否还有其他选择。

4

4 回答 4

2

没有应用程序帮助(提到的标志)的线程取消是一个坏主意。只是谷歌

实际上取消是如此困难,以至于在最新的 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();
};
于 2009-04-30T00:11:12.933 回答
2

您可以使用 pthread_cleanup_push() 将取消清理处理程序推送到线程取消清理堆栈上。该处理程序将负责解锁关键部分。

离开临界区后,您应该调用 pthread_cleanup_pop(0) 将其删除。

IE

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 ,这将执行您的清理代码,而不是自己解除临界区。IE

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.
}
于 2010-02-23T11:11:08.243 回答
1

在不使用定义良好的控制方法(即标志)的情况下中止线程的想法是如此邪恶,以至于您根本不应该这样做。

如果你有第三方代码,除了这样做你别无选择,我可能会建议在进程中抽象出可怕的代码,然后与进程交互,很好地分离每个这样的组件。

现在,这样的设计在windows上会更糟,因为windows不擅长运行多个进程,但在linux上这并不是一个坏主意。

当然,为您的线程模块进行明智的设计会更好......

(就个人而言,我更喜欢根本不使用线程,总是使用进程,或者非阻塞设计)

于 2009-05-07T11:43:51.227 回答
0

如果控制临界区的锁没有直接暴露给你,你无能为力。当您取消一个线程时,该线程的所有清理处理程序都以正常的相反顺序执行,但当然这些处理程序只能释放您有权访问的互斥锁。因此,除了在访问 3rd 方组件期间禁用取消之外,您真的无能为力。

我认为您最好的解决方案是同时使用标志pthread_cancel 功能。当您进入第 3 方组件时,禁用取消处理 (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);
于 2009-03-20T18:01:41.967 回答