一旦可用就获得锁定

时间:2013-04-05 15:27:20

标签: c++ concurrency mutex boost-thread

我有两个线程试图锁定相同的boost::mutex。其中一个线程是连续处理一些数据,另一个是周期性地显示当前状态。根据我的意图,处理线程非常频繁地释放锁并重新获取它,以便显示线程可以在需要时接入并获取它。所以,显然,我希望显示线程在下一次由进程线程释放时获取锁。但是,它没有这样做,相反,它等待锁定,并且只在进程线程的许多锁定释放周期后才获取它。

请检查说明我问题的最小例子:

#include <boost/thread.hpp>
#include <iostream>

using namespace std;
using namespace boost;

mutex mut;

void process() {
        double start = time(0);
        while(1) {
                unique_lock<mutex> lock(mut);
                this_thread::sleep(posix_time::milliseconds(10));
                std::cout<<".";
                if(time(0)>start+10) break;
        }
}

int main() {

        thread t(process);

        while(!t.timed_join(posix_time::seconds(1))) {
                posix_time::ptime mst1 = posix_time::microsec_clock::local_time();
                cout<<endl<<"attempting to lock"<<endl;
                cout.flush();

                unique_lock<mutex> lock(mut);

                posix_time::ptime mst2 = posix_time::microsec_clock::local_time();
                posix_time::time_duration msdiff = mst2 - mst1;
                cout << std::endl<<"acquired lock in: "<<msdiff.total_milliseconds() << endl;
                cout.flush();
        }

}

编译:{{1​​}}

当我运行可执行文件时,示例输出如下:

g++ mutextest.cpp -lboost_thread -pthread

正如您所看到的,在最糟糕的情况下,显示线程在关闭锁定之前会等待424个锁定释放周期。

我显然是以错误的方式使用互斥锁,但解决这个问题的常用方法是什么?

5 个答案:

答案 0 :(得分:4)

并不是说你使用互斥锁是错误的,只是线程没有按照你的期望进行。操作系统决定在哪个线程运行时(这称为“调度”),并且代码中没有任何内容强制在循环结束时进行线程切换;线程继续运行,并重新获取锁定。要尝试的一件事是在释放锁之后添加对this_thread::yield()的调用(在这种情况下,在重新锁定之前,在循环的顶部);这将向调度程序建议它适合另一个线程运行。如果你真的想要紧密同步的交错,那么线程就不会这样做;相反,写一个更高级别的函数,一个接一个地调用你的两个函数。

答案 1 :(得分:1)

如果更新线程没有其他任何操作,则可以等待互斥锁变为可用。

看看boost :: condition_variable。你可以在这里读到它 http://www.boost.org/doc/libs/1_53_0/doc/html/thread/synchronization.html 和这里 Using boost condition variables

如果让更新线程进入睡眠状态会出现问题 - 它在许多GUI系统上并且您没有指定您正在使用哪一个 - 考虑从处理线程发布消息以更新线程。 (同样,细节取决于您的平台。)消息可能包含更新显示所需的信息,或者是“现在是观看的好时机”的通知。

如果您确实使用了条件代码,处理线程可能会在发出条件信号之后和重新获取锁之前为更新线程打开一个大窗口。

答案 2 :(得分:1)

您可能需要查看boost::condition_variable,特别是方法wait()notify_one()notify_all()。方法wait()将阻止当前线程,直到锁被释放。方法notify_one()notify_all()通知一个或所有等待锁继续执行的线程。

答案 3 :(得分:1)

在我看来,互斥的原则不是公平,而是正确。互斥锁本身无法控制调度程序。困扰我的一件事是你选择创建两个使用这种粗粒度锁定的线程的原因。从理论上讲,你并行运行两件事,但事实上你正在使它们相互依赖/串行。

Pete的想法似乎更好(运行这些绘图和更新的一个函数),你仍然可以在每个内部函数中使用线程,而不必担心争用和公平性。

如果你真的希望有两个并行运行的线程,那么我可以给你一些提示:

  • 使用原子的spinlock并忘记互斥,
  • 进入实现定义的土地并设置线程优先级,或
  • 使锁定更精细。

不幸的是,这些都不是问题证明。

答案 4 :(得分:0)

我已经解决了使用条件的问题,正如Dale Wilson和FKaria所建议的那样,但我在相反的方向上使用它。因此,进程线程检查暂停标志,当它设置时,它等待一个条件,因此释放锁。显示线程控制暂停标志,它还通过通知条件恢复进程线程。代码:(代码大致相同,我用//New标记了新行)

#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include <iostream>

using namespace std;
using namespace boost;

mutex mut;
condition cond;

volatile bool shouldPause = false; //New

void process() {
        double start = time(0);
        while(1) {
                unique_lock<mutex> lock(mut);

                if(shouldPause) cond.wait(mut); //New

                this_thread::sleep(posix_time::milliseconds(10));
                std::cout<<".";
                if(time(0)>start+10) break;
        }
}

int main() {

        thread t(process);

        while(!t.timed_join(posix_time::seconds(1))) {
                posix_time::ptime mst1 = posix_time::microsec_clock::local_time();
                cout<<endl<<"attempting to lock"<<endl;
                cout.flush();
                shouldPause = true; // New
                unique_lock<mutex> lock(mut);

                posix_time::ptime mst2 = posix_time::microsec_clock::local_time();
                posix_time::time_duration msdiff = mst2 - mst1;
                cout << std::endl<<"acquired lock in: "<<msdiff.total_milliseconds() << endl;
                cout.flush();

                shouldPause = false; // New
                cond.notify_all(); // New
        }

}

现在输出正如我所希望的那样:

...................................................................................................
attempting to lock
.
acquired lock in: 9
...................................................................................................
attempting to lock
.
acquired lock in: 8
...................................................................................................
attempting to lock
.
acquired lock in: 9
...................................................................................................
attempting to lock
.
acquired lock in: 8
...................................................................................................
attempting to lock
.
acquired lock in: 9
...................................................................................................
attempting to lock
.
acquired lock in: 9
...................................................................................................
attempting to lock
.
acquired lock in: 9
...................................................................................................
attempting to lock
.
acquired lock in: 9
...................................................................................................
attempting to lock
.
acquired lock in: 8
...................................................................................................
attempting to lock
.
acquired lock in: 9
..........................