如果你使用过gui工具包,你就知道有一个事件循环/主循环应该在一切完成后执行,这将使应用程序保持活跃并响应不同的事件.例如,对于Qt,您可以在main()中执行此操作:
int main() {
QApplication app(argc, argv);
// init code
return app.exec();
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,app.exec()是应用程序的主循环.
实现这种循环的显而易见的方法是:
void exec() {
while (1) {
process_events(); // create a thread for each new event (possibly?)
}
}
Run Code Online (Sandbox Code Playgroud)
但是这会将CPU限制在100%并且实际上是无用的.现在,我如何实现这样一个响应的事件循环,而不必完全占用CPU?
回答在Python和/或C++中受到赞赏.谢谢.
脚注:为了学习,我将实现自己的信号/插槽,我会用它们来生成自定义事件(例如go_forward_event(steps)).但是如果你知道如何手动使用系统事件,我也想知道这一点.
我需要两个线程以"tick tock"模式进行.当使用信号量实现时,这看起来很好:
Semaphore tick_sem(1);
Semaphore tock_sem(0);
void ticker( void )
{
while( true )
{
P( tick_sem );
do_tick();
V( tock_sem );
}
}
void tocker( void )
{
while( true )
{
P( tock_sem );
do_tock();
V( tick_sem );
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我使用互斥锁(在技术上是一个二进制信号量)做同样的事情,它有一个奇怪的代码气味.
std::mutex tick_mutex;
std::mutex tock_mutex;
tock_mutex.lock();
void ticker( void )
{
while( true )
{
tick_mutex.lock();
do_tick();
tock_mutex.unlock();
}
}
void tocker( void )
{
while( true )
{
tock_mutex.lock()
do_tock();
tick_mutex.unlock();
}
}
Run Code Online (Sandbox Code Playgroud)
我认为气味是互斥不是为了将信息传达给另一个线程.(c ++ …
条件变量是c ++ 11的一个方面我还在苦苦挣扎.从我收集到的条件变量非常类似于信号量.
但话说回来,信号量不需要锁定才能运行.条件变量可以.而锁又需要一个互斥锁.因此,为了使用信号量的相当简单的功能,我们现在不仅需要管理条件变量.但也是一个互斥锁和一个锁.
那么为什么条件变量需要这个呢?通过添加此要求可以提供哪些附加功能?
我有一个多线程科学应用程序,其中几个计算线程(每个核心一个)必须将其结果存储在公共缓冲区中.这需要互斥机制.
工作线程只花费一小部分时间写入缓冲区,因此互斥锁在大多数时间都处于解锁状态,并且锁定很有可能立即成功而无需等待另一个线程解锁.
目前,我已经使用Qt的QMutex执行任务,并且运行良好:互斥锁的开销可以忽略不计.
但是,我必须将它移植到c ++ 11/STL.使用std :: mutex时,性能下降了66%,并且线程花费大部分时间来锁定互斥锁.
在另一个问题之后,我认为Qt使用基于简单原子标志的快速锁定机制,针对互斥锁尚未锁定的情况进行了优化.并发锁定发生时会回退到系统互斥锁.
我想在STL中实现这一点.是否有一种基于std :: atomic和std :: mutex的简单方法?我已经深入研究了Qt的代码,但对于我的使用似乎过于复杂(我不需要锁定超时,pimpl,小占用空间等......).
编辑:我试过一个螺旋锁,但这不能很好地工作,因为:
定期(每隔几秒),另一个线程锁定互斥锁并刷新缓冲区.这需要一些时间,因此此时所有工作线程都会被阻止.自旋锁使调度繁忙,导致刷新比使用适当的互斥锁慢10-100倍.这是不可接受的
编辑:我试过这个,但它不起作用(锁定所有线程)
class Mutex
{
public:
Mutex() : lockCounter(0) { }
void lock()
{
if(lockCounter.fetch_add(1, std::memory_order_acquire)>0)
{
std::unique_lock<std::mutex> lock(internalMutex);
cv.wait(lock);
}
}
void unlock();
{
if(lockCounter.fetch_sub(1, std::memory_order_release)>1)
{
cv.notify_one();
}
}
private:
std::atomic<int> lockCounter;
std::mutex internalMutex;
std::condition_variable cv;
};
Run Code Online (Sandbox Code Playgroud)
谢谢!
编辑:最终解决方案
MikeMB的快速互斥体工作得非常好.
作为最终的解决方案,我做了:
我有一个偶尔从GigE相机获得一个帧的功能,并希望它快速返回.标准程序是这样的:
// ...
camera.StartCapture();
Image img=camera.GetNextFrame();
camera.StopCapture(); // <-- takes a few secs
return img;
Run Code Online (Sandbox Code Playgroud)
返回数据准备就绪GetNextFrame()并且StopCapture()非常慢; 因此,我想img尽快返回,并产生一个后台线程StopCapture().但是,在(不太可能)再次启动采集的情况下,我希望通过互斥锁保护访问.有些地方可以抛出异常,所以我决定使用RAII风格的锁,它将在范围退出时释放.同时,我需要将锁转移到后台线程.像这样的东西(伪代码):
class CamIface{
std::mutex mutex;
CameraHw camera;
public:
Image acquire(){
std::unique_lock<std::mutex> lock(mutex); // waits for cleanup after the previous call to finish
camera.StartCapture();
Image img=camera.GetNextFrame();
std::thread bg([&]{
camera.StopCapture(); // takes a long time
lock.release(); // release the lock here, somehow
});
bg.detach();
return img;
// do not destroy&release lock here, do it in the bg thread …Run Code Online (Sandbox Code Playgroud) 在下面的代码中,main()函数调用request()函数,该函数调用th_request_async()函数,其中mm_th_done_cb().
只有在执行mm_th_done_cb()之后,才能在main中继续进行最佳和最有效的方法.
DUMMY CODE
int mm_th_done_cb(int error_code, th_result_s* th_result, void* user_data)
{
return 0;
}
void request()
{
th_request_s MyItemInfo;
strncpy(MyItemInfo.origin_path, szUrl, 1024+1);
MyItemInfo.orientation = 0;
MyItemInfo.func = mm_th_done_cb;
MyItemInfo.used_cache = 1;
th_request_async(MyItemInfo);
}
int main()
{
request();
// Here I need to do something only after mm_th_done_cb() has been excuted.
}
Run Code Online (Sandbox Code Playgroud) 在下面的例子中(理想化的"游戏")有两个线程.更新数据并将RenderThread其"呈现"到屏幕的主线程.我需要的是那两个要同步的东西.我没有能力运行几次更新迭代而不为它们中的每一个运行渲染.
我使用a condition_variable来同步这两个,所以理想情况下,更快的线程将花费一些时间等待更慢.但是,如果其中一个线程在很短的时间内完成迭代,则条件变量似乎不起作用.它似乎很快就会重新获取互斥锁的锁定,然后wait另一个线程才能获取它.即使notify_one被称为
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <functional>
#include <mutex>
#include <condition_variable>
using namespace std;
bool isMultiThreaded = true;
struct RenderThread
{
RenderThread()
{
end = false;
drawing = false;
readyToDraw = false;
}
void Run()
{
while (!end)
{
DoJob();
}
}
void DoJob()
{
unique_lock<mutex> lk(renderReadyMutex);
renderReady.wait(lk, [this](){ return readyToDraw; });
drawing = true;
// RENDER DATA
this_thread::sleep_for(chrono::milliseconds(15)); // simulated render time
cout << "frame " …Run Code Online (Sandbox Code Playgroud) 我注意到提升似乎不支持信号量.获得类似效果的最简单方法是什么?
我有一个C++ 11应用程序,它具有生成数据的高优先级线程,以及一个消耗它的低优先级线程(在我的情况下,将其写入磁盘).我想确保高优先级的生产者线程永远不会被阻止,即它只使用无锁算法.
使用无锁队列,我可以从生产者线程将数据推送到队列,并从消费者线程中轮询它,从而实现上述目标.我想修改我的程序,以便消费者线程在非活动状态而不是轮询时阻塞.
似乎C++ 11条件变量可能对阻塞使用者线程很有用.有人能告诉我一个如何使用它的例子,同时避免消费者睡觉时数据仍在队列中吗?更具体地说,我想确保在生产者将最后一个项目推入队列后,消费者总是在某个有限的时间内被唤醒.生产者保持非阻塞也很重要.
本网站上的一些问题涉及C++ 11中引入的多线程支持中缺少信号量对象.许多人建议使用互斥锁或条件变量或两者的组合来实现信号量.
但是,这些方法都不允许增加和减少信号量,同时保证调用线程不被阻塞,因为通常必须在读取信号量值之前获取锁定.例如,POSIX信号量具有函数,sem_post()并且sem_trywait()两者都是非阻塞的.
是否可以仅使用C++ 11多线程支持实现非阻塞信号量?或者我是否一定要为此使用依赖于操作系统的库?如果是这样,为什么C++ 11修订版不包含信号量对象?
一个类似的问题在3年内没有得到回答.(注意:我相信我要问的问题要广泛得多,除了生产者/消费者之外,还有一些非阻塞信号量对象的其他用途.如果有人认为我的问题是重复的,请告诉我如何我可以回想一下旧问题,因为这仍然是一个悬而未决的问题.)
c++ ×9
c++11 ×6
mutex ×3
concurrency ×2
blocking ×1
boost ×1
boost-thread ×1
event-loop ×1
flags ×1
python ×1
qt ×1
semaphore ×1
stl ×1