我正在通过使其更加异步来改进应用程序(Win64,C++).我正在使用并发运行时,到目前为止它对我来说非常有用.
该应用程序基本上执行了许多转换数据的"作业".为了跟踪每个作业的作用,某些子系统配有代码来跟踪作业执行的某些操作.以前,这将使用表示当前正在执行的作业的单个全局变量,以便能够注册跟踪信息,而无需在调用链中一直传递上下文信息.每个作业也可以使用ConcRT来并行化作业本身.一切都很好.
现在,我正在重构应用程序,以便我们可以并行执行顶级作业.每个作业都作为ConcRT任务执行,这适用于除需要跟踪的作业之外的所有作业.
我基本上需要的是一种将一些上下文信息与任务相关联的方法,并将该流程交给该任务产生的任何其他任务.基本上,我需要"任务本地"变量.
使用ConcRT,我们不能简单地使用线程本地来存储上下文信息,因为作业可能使用ConcRT生成其他作业,并且这些作业将在任意数量的线程上执行.
我当前的方法涉及在启动时创建许多Scheduler实例,并在专用于该作业的调度程序中生成每个作业.然后我可以使用该Concurrency::CurrentScheduler::Id()函数来检索整数ID,我可以将其用作关键来计算上下文.这可以工作,但单步进入Concurrency::CurrentScheduler::Id()程序集使我有点畏缩,因为它执行多个虚函数调用和安全检查,这增加了相当多的开销,这是一个问题,因为这个查找需要在极高的情况下完成在某些情况下.
那么 - 有没有更好的方法来实现这一目标?我本来希望有一个一流的TaskLocal/userdata机制,它允许我将单个上下文指针与当前的Scheduler/SchedulerGroup/Task相关联,我可以用很少的开销检索它.
每当ConcRT线程抓取新任务时调用的钩子将是我的理想选择,因为我可以检索Scheduler/ScheduleGroup ID并将其存储在本地线程中以获得最小的访问开销.唉,我看不到任何方式来注册这样的钩子,似乎不可能为PPL /代理实现自定义的Scheduler类(参见这篇文章).
我想使用Visual Studio 2010的并发库来在线程之间传递操作.我有我的课程,SimpleAction并指向它存储在Concurrency::concurrent_queue.
使用这个定义和"消费"逻辑它可以工作:
typedef Concurrency::concurrent_queue<SimpleAction *> ActionQueue;
while (true)
{
SimpleAction *action = nullptr;
while (m_queue.try_pop(action))
{
action->process();
delete action;
}
Sleep(100);
}
Run Code Online (Sandbox Code Playgroud)
但是,当我将其更改为std :: unique_ptr时,如下所示:
typedef Concurrency::concurrent_queue<std::unique_ptr<SimpleAction>> ActionQueue;
while (true)
{
std::unique_ptr<SimpleAction> action;
while (m_queue.try_pop(action))
{
action->process();
}
Sleep(100);
}
Run Code Online (Sandbox Code Playgroud)
编译器给出以下错误消息:
Run Code Online (Sandbox Code Playgroud)F:\DevStudio\Vs2010\VC\INCLUDE\concurrent_queue.h(366) : error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' with [ _Ty=`anonymous-namespace'::SimpleAction ] F:\DevStudio\Vs2010\VC\INCLUDE\memory(2347) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr' with [ _Ty=`anonymous-namespace'::SimpleAction ] F:\DevStudio\Vs2010\VC\INCLUDE\concurrent_queue.h(365) : while compiling class …
我目前正在使用Microsoft的并发运行时中的异步代理库.我还没有找到一种明显的方法来通过使用窗口消息或其他一些通知UI线程完成工作的方式来表示任务已完成.
我知道我可以将窗口句柄和消息值(WM_xxx)传递给任务,并让任务用于PostMessage()发出UI线程的信号.在我看来,这有点难看,也是错误的根源.如果发生异常,我必须有一个catch处理程序来指示我的UI线程.这很容易被遗忘,并且异常情况可能不会经常运行,因此很难发现它.
该文档讨论了如何将数据移回UI线程.它不使用窗口消息,而是使用轮询技术.当有"中断"方法可用时,我觉得设置定时器以查询任务是否完成是愚蠢的!
这有点奇怪,它没有内置到库中,因为它不是跨平台库.根据我的理解,它被设计为仅在Windows和Windows上运行.
库中是否有可用的功能,还是我必须手动滚动?
您能告诉我,我用来处理用例的方法是否无效,如果是,那么处理的正确方法是什么:
task<int> do_work(int param)
{
// runs some work on a separate thread, returns task with result or throws exception on failure
}
void foo()
{
try
{
auto result_1 = do_work(10000);
auto result_2 = do_work(20000);
// do some extra work
process(result_1.get(), result_2.get());
}
catch (...)
{
// logs the failure details
}
}
Run Code Online (Sandbox Code Playgroud)
因此,代码尝试并行执行两个作业,然后处理结果.如果其中一个作业抛出异常,则调用task::get将重新抛出异常.如果两个任务都抛出异常,则会出现问题.在这种情况下,第一次调用task::get将导致堆栈展开,因此第二次task调用将被调用,并且反过来会导致在堆栈展开期间重新抛出一个异常,从而导致调用"abort".
在我面对这个问题之前,这种方法对我来说似乎完全有效.
维基百科将执行单元定义为:
“在计算机工程中,执行单元(也称为功能单元)是 CPU 的一部分,它执行计算机程序所要求的操作和计算。”
现在,执行程序的操作是合乎逻辑的还是概念上的?或者它是 CPU 中执行程序要求的任务(例如关闭计算机、更改颜色等)的物理(硬件)结构?
我读过“在并发编程中,有两个执行单元,即进程和线程。”
现在,我在脑海中提出的概念是,执行单元是一个相关类的包以及它们使用的系统资源,例如系统内存和其他资源。
请告诉我在多大程度上我是对的?
注意:请保持您的语言(即您可能使用的行话和术语)简单到初学者能够理解。
先感谢您。
java concurrency execution execution-time concurrency-runtime