0

In C++20 we got a new thread-class std::jthread that unlike old std::thread waits for thread termination in the destructor. So it becomes easy to execute several actions in parallel using unnamed jthread-objects and comma operator:

#include <thread>
#include <iostream>

int main() {
    std::jthread{ []{ std::cout << "Hello from new thread\n"; } },
        std::cout << "Hello from main thread\n";
}

This works fine, demo: https://gcc.godbolt.org/z/YPW8bq7jK

Unfortunately, Visual Studio 2019 issues a warning here:

warning C4834: discarding return value of function with 'nodiscard' attribute

because std::jthread constructor is declared as [[nodiscard]] in it. As far as I see the standard does not require this attribute: https://timsong-cpp.github.io/cppwp/n4861/thread.jthread.class.

Is it just a bug in MSVC STL, or there is really some risk associated with such jthread-usage?

Fedor
  • 17,146
  • 13
  • 40
  • 131
  • 2
    Its a warning. You can ignore it. Its a bad idea to ignore it; but you can. It is not a bug in MSVC as it is just providing diagnostic information. You are treating an object like a function call. You are creating an object (in this case a temporary object). Abusing the comma operator to print out a value (that may or may not happen before the thread is complete). Why not just create an automatic variable. – Martin York Sep 02 '21 at 16:43
  • 1
    See: Abuse of comma operator. https://stackoverflow.com/q/17902992/14065 – Martin York Sep 02 '21 at 16:53
  • All warnings exist for the sole purpose of catching unintentional bugs. Microsoft's STL in particular has [over 3000 places](https://twitter.com/StephanTLavavej/status/1151977361207771136?t=Qmkj_Y4EkwYnJiWE1ijE9w&s=19) that try to spare you grief. – StoryTeller - Unslander Monica Sep 02 '21 at 17:18
  • @StoryTeller-UnslanderMonica Not all, but most warnings technically. There are a few "warnings" that are merely informative. – eerorika Sep 02 '21 at 18:17
  • Thanks, it is clear that warnings can be ignored/suppressed. The question is more about whether an STL implementation can insert additional `[[nodiscard]]` attributes on top of what the standard demands? Is it allowed or shall be considered as a bug? Especially considering that one is forced to suppress all `[[nodiscard]]` and not only additional ones. – Fedor Sep 02 '21 at 19:18
  • Conformance in the eyes of the standard is to issue a diagnostic for an ill-formed program, and provide a translated well-formed program that behaves a certain way. That's it. Issuing warnings while compiling does not violate either tenet. The standard doesn't care what warnings you deal with against an implementation. – StoryTeller - Unslander Monica Sep 02 '21 at 20:42

2 Answers2

2

Why not write it like this?

#include <thread>
#include <iostream>

int main()
{
     // Note: Just like the original
     //       It is indeterminate what order these are
     //       printed out in. 
     std::jthread  parallelThread{ []{ std::cout << "Hello from new thread\n"; } };
     std::cout << "Hello from main thread\n";
}

This does exactly the same, but uses a named variable rather an invisible temporary.

As a side note you can force order by making sure you know when the jthread is destroyed.

#include <thread>
#include <iostream>

int main()
{
     {
         std::jthread  parallelThread{ []{ std::cout << "Hello from new thread\n"; } };
     }
     std::cout << "Hello from main thread\n";
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thanks. In your second example, there will be a sequential execution: `parallelThread` will be started and main thread will not do anything until `parallelThread` is finished. It is hardly an expected behavior. – Fedor Sep 02 '21 at 17:21
  • @Fedor What do you mean? You said in your original post that `jthread` waits in the destructor. So that is by definition what I would expect to happen. If I want a bunch of parallel threads (just create them in a sub block) then in the main thread I know they are all done when the block exits. – Martin York Sep 02 '21 at 17:30
  • 1
    Yes, it waits in the destructor, but at the end of entire comma operator, and not at the end of its part: https://gcc.godbolt.org/z/P7n7Mx617 – Fedor Sep 02 '21 at 17:33
  • 1
    Yes. I understand. The original version points that out. That the parallel thread and the main thread run together, so it is indeterminate which one is printed first (just like your comma operator (abusive) version). So the second one shows you how to force an order. – Martin York Sep 02 '21 at 17:38
2

there is really some risk associated with such jthread-usage?

Define "such usage" and "some risk".

The nodiscard attribute is entirely appropriate for jthread's constructors. Yes, you can write code where creating and discarding a jthread object is a meaningful, functional thing. But even if it is meaningful and functional, you still shouldn't do it. It's being cute in your code in a way that adds absolutely nothing of value. It looks like a bug, and its best not to write code that looks almost identical to broken code.

Try to write clear, clean code that doesn't require someone to know the intimate details of the C++ comma operator and its relationship to subexpressions in order to accurately gauge its operation.

And yes, standard library implementations are allowed to give warnings for any reason. So long as well-formed programs successfully compile and execute as described by the standard, the implementation is valid.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982