使用线程池的std :: async的Visual C ++实现是否合法 [英] Is the Visual C++ implementation of std::async using a thread pool legal

查看:76
本文介绍了使用线程池的std :: async的Visual C ++实现是否合法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在用std::launch::async调用std::async时,Visual C ++使用Windows线程池(如果可用,则使用Windows Vista的CreateThreadpoolWork,如果没有,则使用QueueUserWorkItem).

Visual C++ uses the Windows thread pool (Vista's CreateThreadpoolWork if available and QueueUserWorkItem if not) when calling std::async with std::launch::async.

池中的线程数是有限的.如果创建多个长时间运行而不会休眠的任务(包括执行I/O),则队列中即将出现的任务将没有机会工作.

The number of threads in the pool is limited. If create several tasks that run for a long time without sleeping (including doing I/O), the upcoming tasks in the queue won't get a chance to work.

标准(我正在使用N4140)说,将std::asyncstd::launch::async一起使用

The standard (I'm using N4140) says that using std::async with std::launch::async

...调用INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...)(20.9.2,30.3.1.2),就像在由线程对象表示的新执行线程中,在名为async的线程.

... calls INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...) (20.9.2, 30.3.1.2) as if in a new thread of execution represented by a thread object with the calls to DECAY_COPY() being evaluated in the thread that called async.

(§30.6.8p3,重点是我的.)

(§30.6.8p3, Emphasis mine.)

std::thread的构造函数创建一个新线程等.

std::thread's constructor creates a new thread etc.

一般来说,关于线程(第1.10p3节):

About threads in general it says (§1.10p3):

实现应确保所有不受阻塞的线程最终都能取得进展. [注意:标准库函数可能会在I/O或锁上以静默方式阻塞.执行环境中的因素(包括外部施加的线程优先级)可能会阻止实现对前进的某些保证. — 尾注]

Implementations should ensure that all unblocked threads eventually make progress. [Note: Standard library functions may silently block on I/O or locks. Factors in the execution environment, including externally-imposed thread priorities, may prevent an implementation from making certain guarantees of forward progress. —end note]

如果我创建了一堆OS线程或std::thread,它们全部执行一些非常长的(也许是无限的)任务,那么它们都将被调度(至少在Windows上;不会弄乱优先级,关联性等). .如果我们将相同的任务计划到Windows线程池中(或使用std::async(std::launch::async, ...)来执行),则稍后的计划任务将不会运行,直到较早的任务完成为止.

If I create a bunch of OS threads or std::threads, all performing some very long (perhaps infinite) tasks, they'll all be scheduled (at least on Windows; without messing with priorities, affinities, etc.). If we schedule the same tasks to the Windows thread pool (or use std::async(std::launch::async, ...) which does that), the later scheduled tasks won't run until the earlier tasks will finish.

严格来讲,这合法吗? 最终"是什么意思?

Is this legal, strictly speaking? And what does "eventually" mean?

问题在于,如果首先安排的任务是无限的,则其余任务将无法运行.因此,其他线程(不是OS线程,而是按照as-if规则的"C ++线程")将不会取得进展.

The problem is that if the tasks scheduled first are de-facto infinite, the rest of the tasks won't run. So the other threads (not OS threads, but "C++-threads" according to the as-if rule) won't make progress.

有人可能会争辩说,如果代码具有无限循环,则行为是不确定的,因此是合法的.

One may argue that if the code has infinite loops the behavior is undefined, and thus it's legal.

但是我认为我们不需要标准说明的导致UB实现这一问题的无限循环.访问易失性对象,执行原子操作和同步操作都是禁用"循环终止假设的副作用.

But I argue that we don't need an infinite loop of the problematic kind the standard says causes UB to make that happen. Accessing volatile objects, performing atomic operation and synchronization operations are all side effects that "disable" the assumption about loops terminating.

((我有一堆异步调用执行以下lambda

(I have a bunch of async calls executing the following lambda

auto lambda = [&] {
    while (m.try_lock() == false) {
        for (size_t i = 0; i < (2 << 24); i++) {
            vi++;
        }
        vi = 0;
    }
};

,只有在用户输入时才释放锁定.但是还有其他有效类型的合法无限循环.)

and the lock is released only upon user input. But there are other valid kinds of legitimate infinite loops.)

如果我安排了几个这样的任务,那么我安排的任务将无法运行.

If I schedule a couple of such tasks, tasks I schedule after them don't get to run.

一个非常邪恶的例子将是启动太多的任务,这些任务一直运行到释放锁/标志升起,然后使用`std :: async(std :: launch :: async,...)进行调度,升起国旗.除非单词最终"表示非常令人惊讶,否则该程序必须终止.但是在VC ++实现下,不会!

A really wicked example would be launching too many tasks that run until a lock is release/a flag is raised and then schedule using `std::async(std::launch::async, ...) a task that raises the flag. Unless the word "eventually" means something very surprising, this program has to terminate. But under the VC++ implementation it won't!

在我看来,这似乎违反了标准.使我感到奇怪的是笔记中的第二句话.某些因素可能会阻止实现对前进的进展做出某些保证.那么这些实现如何符合要求?

To me it seems like a violation of the standard. What makes me wonder is the second sentence in the note. Factors may prevent implementations from making certain guarantees of forward progress. So how are these implementation conforming?

这就像是说,可能有一些因素阻止实现提供某些方面的内存排序,原子性,甚至存在多个执行线程.不错,但是合规的托管实现必须支持多个线程.对他们及其因素而言太糟糕了.如果他们不能提供,那不是C ++.

It's like saying there may be factors preventing implementations from providing certain aspect of memory ordering, atomicity, or even the existence of multiple threads of execution. Great, but conforming hosted implementations must support multiple threads. Too bad for them and their factors. If they can't provide them that's not C++.

这是放宽要求吗?如果这样解释,那就是完全撤消了需求,因为它没有指定影响因素,更重要的是,实现可能未提供哪些保证.

Is this a relaxation of the requirement? If interpreting so, it's a complete withdrawal of the requirement, since it doesn't specify what are the factors and, more importantly, which guarantees may be not supplied by the implementations.

如果不是-那个音符甚至是什么意思?

If not - what does that note even mean?

我记得根据ISO/IEC指令,脚注是非规范性的,但我不确定该注释.我确实在ISO/IEC指令中发现以下内容:

I recall footnotes being non-normative according to the ISO/IEC Directives, but I'm not sure about notes. I did find in the ISO/IEC directives the following:

24个注释

24.1目的或理由

注释用于提供其他信息,以帮助理解或使用文档的文本. 该文档无需注释即可使用.

Notes are used for giving additional information intended to assist the understanding or use of the text of the document. The document shall be usable without the notes.

强调我的.如果我认为文档没有特别说明,在我看来线程必须取得进展,std::async(std::launch::async, ...)的作用是-如果,仿函数是在新线程上执行的,就好像它正在被执行一样.使用std::thread创建的函子,因此使用std::async(std::launch::async, ...)分配的函子必须取得进展.在带有线程池的VC ++实现中,它们没有.因此VC ++在这方面违反了标准.

Emphasis mine. If I consider the document without that unclear note, seems to me like threads must make progress, std::async(std::launch::async, ...) has the effect as-if the functor is execute on a new thread, as-if it was being created using std::thread, and thus a functors dispatched using std::async(std::launch::async, ...) must make progress. And in the VC++ implementation with the threadpool they don't. So VC++ is in violation of the standard in this respect.

完整示例,已在i5-6440HQ的Windows 10 Enterprise 1607上使用VS 2015U3进行了测试:

Full example, tested using VS 2015U3 on Windows 10 Enterprise 1607 on i5-6440HQ:

#include <iostream>
#include <future>
#include <atomic>

int main() {
    volatile int vi{};
    std::mutex m{};
    m.lock();

    auto lambda = [&] {
        while (m.try_lock() == false) {
            for (size_t i = 0; i < (2 << 10); i++) {
                vi++;
            }
            vi = 0;
        }
        m.unlock();
    };

    std::vector<decltype(std::async(std::launch::async, lambda))> v;

    int threadCount{};
    std::cin >> threadCount;
    for (int i = 0; i < threadCount; i++) {
        v.emplace_back(std::move(std::async(std::launch::async, lambda)));
    }

    auto release = std::async(std::launch::async, [&] {
        __asm int 3;
        std::cout << "foo" << std::endl;
        vi = 123;
        m.unlock();
    });

    return 0;
}

等于或小于4终止.超过4个则没有.

With 4 or less it terminates. With more than 4 it doesn't.

类似的问题:

  • Is there an implementation of std::async which uses thread pool? - But it doesn't question about legality, and doesn't have an answer anyway.

std :: async-实现依赖用法?-提及线程池并没有真正得到支持",而是着重于thread_local变量(即使答案和评论都说,即使不是简单"或非平凡的,也可以解决),并且在取得进展的要求附近未解决注释.

std::async - Implementation dependent usage? - Mentions that "thread pools are not really supported" but focuses on thread_local variables (which is solvable even if "not straightforward" or non-trivial as the answer and comment say) and doesn't address the note near the requirement of making progress.

推荐答案

在C ++ 17中,

The situation has been clarified somewhat in C++17 by P0296R2. Unless the Visual C++ implementation documents that its threads do not provide concurrent forward progress guarantees (which would be generally undesirable), the bounded thread pool is not conforming (in C++17).

有关从外部施加的线程优先级"的注释已被删除,这也许是因为环境已经总是有可能阻止C ++程序的进展(如果没有优先级,则通过挂起,如果不是,然后由于电源或硬件故障).

The note about "externally imposed thread priorities" has been removed, perhaps because it is already always possible for the environment to prevent the progress of a C++ program (if not by priority, then by being suspended, and if not that, then by power or hardware failure).

该部分中还有一个剩余的规范性应该",但与之相关(如conio

There is one remaining normative "should" in that section, but it pertains (as conio mentioned) only to lock-free operations, which can be delayed indefinitely by frequent concurrent access by other thread to the same cache line (not merely the same atomic variable). (I think that in some implementations this can happen even if the other threads are only reading.)

这篇关于使用线程池的std :: async的Visual C ++实现是否合法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆