Der folgende Code erzeugt eine Warnung, wenn er mit Thread Sanitizer unter macOS ausgeführt wird. Ich kann nicht sehen, wo das Rennen ist. Der Steuerblock von shared_ptr und schwach_ptr ist threadsicher, und das Drücken und Herausspringen von wird durch Halten std::queue
einer Sperre erfolgen.
#include <future>
#include <memory>
#include <queue>
class Foo {
public:
Foo() {
fut = std::async(std::launch::async, [this] {
while (!shouldStop) {
std::scoped_lock lock(mut);
while (!requests.empty()) {
std::weak_ptr<float> requestData = requests.front();
requests.pop();
(void)requestData;
}
}
});
}
~Foo() {
shouldStop.store(true);
fut.get();
}
void add(const std::weak_ptr<float> subscriber) {
std::scoped_lock lock(mut);
requests.push(subscriber);
}
private:
std::atomic<bool> shouldStop = false;
std::future<void> fut;
std::queue<std::weak_ptr<float>> requests;
std::mutex mut;
};
int main() {
Foo foo;
int numIterations = 100000;
while (--numIterations) {
auto subscriber = std::make_shared<float>();
foo.add(subscriber);
subscriber.reset();
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
Warnung mit Stacktrace:
WARNING: ThreadSanitizer: data race (pid=11176)
Write of size 8 at 0x7b0800000368 by thread T1 (mutexes: write M16):
#0 operator delete(void*) <null>:1062032 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x4f225)
#1 std::__1::__shared_ptr_emplace<float, std::__1::allocator<float> >::__on_zero_shared_weak() new:272 (minimal:x86_64+0x1000113de)
#2 std::__1::weak_ptr<float>::~weak_ptr() memory:5148 (minimal:x86_64+0x100010762)
#3 std::__1::weak_ptr<float>::~weak_ptr() memory:5146 (minimal:x86_64+0x100002448)
#4 Foo::Foo()::'lambda'()::operator()() const minimal_race.cpp:15 (minimal:x86_64+0x10000576e)
#5 void std::__1::__async_func<Foo::Foo()::'lambda'()>::__execute<>(std::__1::__tuple_indices<>) type_traits:4345 (minimal:x86_64+0x1000052f0)
#6 std::__1::__async_func<Foo::Foo()::'lambda'()>::operator()() future:2323 (minimal:x86_64+0x100005268)
#7 std::__1::__async_assoc_state<void, std::__1::__async_func<Foo::Foo()::'lambda'()> >::__execute() future:1040 (minimal:x86_64+0x100005119)
#8 void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<Foo::Foo()::'lambda'()> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<Foo::Foo()::'lambda'()> >*> >(void*) type_traits:4286 (minimal:x86_64+0x10000717c)
Previous atomic write of size 8 at 0x7b0800000368 by main thread:
#0 __tsan_atomic64_fetch_add <null>:1062032 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x24cdd)
#1 std::__1::shared_ptr<float>::~shared_ptr() memory:3472 (minimal:x86_64+0x1000114d4)
#2 std::__1::shared_ptr<float>::~shared_ptr() memory:4502 (minimal:x86_64+0x100002488)
#3 main memory:4639 (minimal:x86_64+0x10000210b)
SUMMARY: ThreadSanitizer: data race new:272 in std::__1::__shared_ptr_emplace<float, std::__1::allocator<float> >::__on_zero_shared_weak()
edit: Ich kompiliere es mit:
clang++ -std=c++17 -g -fsanitize=thread -o test minimal_race.cpp
Clang-Version:
$ clang++ --version
clang version 7.1.0 (tags/RELEASE_710/final)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm@7/bin
Ich benutze macOS 10.14.6
c++
multithreading
shared-ptr
weak-ptr
thread-sanitizer
tuple_cat
quelle
quelle
std::launch::async
können Siestd::async
festlegen, wie Ihre Anfragen gemäß cppreference.com geplant werden sollen . Dies bedeutet , dass möglicherweise beiFoo
aufgebaut ist, diefuture
den Mutex sperrt (was es nicht entsperren tut bisshouldStop
wahr ist ). WennFoo::Add
es dann aufgerufen wird, wird es versuchen, den Mutex zu sperren, und darauf warten, dass die Zukunft ihn entsperrt, was es niemals tut.clang++ -std=c++17 -O0 -ggdb -fsanitize=thread -stdlib=libc++ -o x x.cpp
, passiert nicht mit libstdc ++,Antworten:
Ich denke, dies könnte ein Fehler mit libc ++ sein und in ihrer Implementierung für schwaches_ptr / gemeinsames_ptr klirren ...
Durch Ändern der Warteschlange für schwache_PTRs in eine gemeinsam genutzte_PTR-Warteschlange wird das Problem auch bei alten Clang-Versionen behoben.
Änderungen sind Zeile 25:
Zeile 33:
und das während in Zeile 42 umschreiben als:
quelle
shared_ptr
.