传递多线程数据的最佳设计实践?
Best Design Practices for Passing Around Multithread Data?
我正在尝试创建一个干净高效的设计,用于将事件传递给后台线程进行评估,然后 return 将选定的结果传递给游戏线程。
这是我的初步设计
//Occurrence object passed from director on game thread to background thread OccurrenceQueue
//Execute BackgroundThread::EvaluateQueues()
//If OccurrenceQueue.Dequeue()
//Score Occurrence, then return via Occurrence.GetOwner()->PurposeSelected(Occurrence)
//EvaluateQueues()
这导致了从一个事件中选择一系列目的的干净循环。所以现在我想把它移到后台线程。这是我到目前为止学到的东西:
线程安全(在UE中)绝对不需要修改UObject
来自其他线程的数据(根据我的阅读,这是由于他们的习惯
GC)
- 您可以锁定对象 and/or 设计以便对象在背景中
线程未被游戏线程触及,但仍有
的风险
由于生命周期未被背景延长而导致的意外行为
线程和同步问题
您不能简单地在游戏线程现有对象上执行函数
将调用堆栈移回游戏线程
从一个调用 Occurrence.GetOwner()->PurposeSelected(Occurrence)
后台线程留在后台线程
- 这是我想更好地理解的主要主题
这也适用于 UE 中的代表
UE中的TQueues可以跨线程安全使用
根据我上面的了解,我目前的设计在逻辑上似乎是不可能的。
到目前为止,这些是我的替代方案:
使用两个队列
一个在后台线程上出列和评分
另一个通过 tick
将游戏线程上的结果出列
使用游戏线程中存在的委托,通过 tick
调用 OccurrenceEvaluated.Broadcast()
- 对结果进行评分后,将 Occurrence.GetOwner()->PurposeSelected(Occurrence) 绑定到 OccurrenceEvaluated
我已经看到 c++ 使用称为 Future() 的东西(或类似的东西)来执行 ASync 任务,而且 UE 似乎与 TFuture<> && TFuture.IsReady( ),但我还没有深入研究它以及它如何 returns 数据
- 与 FAsyncTask 相同
我犹豫要不要实施任何利用 tick 来检查数据是否 updated/returned 来自后台线程的设计。
任何人都可以建议相关的设计实践,或者阐明 return 从后台线程执行到主线程的性质(我很难找到 research/info 的正确问题关于这个)?
早上好,
线程可以帮助您完成工作 faster/more 响应迅速但有很多陷阱。在这种情况下的简短回答我使用这些结构
#include <future>
#include <thread>
#include <vector>
int main()
{
// make data shared
auto data = std::make_shared<std::vector<int>>();
data -> push_back(42);
// future will be a future<int> deduced from return from vector
// async will run lambda in other thread
// capture data by value! It copies the shared_ptr and will
// increase its reference count.
auto future = std::async(std::launch::async, [data]
{
auto value = data[0];
return value;
// shared_ptr data goes out of scope and reference count to data is decreased
});
// synchronize with thread and get "calculated" value
auto my_value = future.get();
// now data on the main thread goes out of scope reference count is decreased. Only when both the thread are done AND this function exits then data is deleted.
return 0;
}
- 大多数对象都会占用内存,并且在分配期间内存不会在 1 个时钟周期内更新。您可以使用 sdt::mutex 和 std::scoped_lock 来保护内存。尝试寻找那些。
- 你经常需要在线程之间同步information/processing,看看std::condition_variable(但是他们有坑https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables)
- C++ 对 类、std::thread 和 std::async/std::future 有很好的支持。我个人喜欢 std::future 的解决方案,因为你可以 return 值和异常
(!) 当您调用 future.get()
时来自其他线程
- 了解 lambda 和捕获,它们对于与 std::thread/std::async
一起使用至关重要
- 对象的生命周期!在线程之间共享对象时,您必须确保一个线程不会删除其他线程正在使用的对象。使用线程时不要使用原始指针 and/or new/delete。我个人经常在线程之间共享信息时使用 std::make_shared/std::shared_ptr 到 data/objects。
- 另一个棘手的事情是,有时您无法确定另一个线程中的工作在创建后是否已经开始。例如。当 std::async returns 一个线程被创建但它不能保证真正开始(操作系统调度等...)。如果您想真正确定它已经启动,那么在异步调用 returns 之后,您将不得不等待您在线程函数开始时设置的 condition_variable。
我希望这些评论可以帮助您入门。
我找到了一个完美的解决方案。由于这些事件对时间不是特别敏感,我只是使用 Unreal Engine 的 AsyncTask() 从我的后台线程在游戏线程上安排一个异步任务。正如@Pepjin Kramer 指出的那样,与 std::async.
相同
这么简单,基本就是一记耳光
我正在尝试创建一个干净高效的设计,用于将事件传递给后台线程进行评估,然后 return 将选定的结果传递给游戏线程。
这是我的初步设计
//Occurrence object passed from director on game thread to background thread OccurrenceQueue
//Execute BackgroundThread::EvaluateQueues()
//If OccurrenceQueue.Dequeue()
//Score Occurrence, then return via Occurrence.GetOwner()->PurposeSelected(Occurrence)
//EvaluateQueues()
这导致了从一个事件中选择一系列目的的干净循环。所以现在我想把它移到后台线程。这是我到目前为止学到的东西:
线程安全(在UE中)绝对不需要修改UObject 来自其他线程的数据(根据我的阅读,这是由于他们的习惯 GC)
- 您可以锁定对象 and/or 设计以便对象在背景中
线程未被游戏线程触及,但仍有
的风险 由于生命周期未被背景延长而导致的意外行为 线程和同步问题
- 您可以锁定对象 and/or 设计以便对象在背景中
您不能简单地在游戏线程现有对象上执行函数 将调用堆栈移回游戏线程
从一个调用 Occurrence.GetOwner()->PurposeSelected(Occurrence) 后台线程留在后台线程
- 这是我想更好地理解的主要主题
这也适用于 UE 中的代表
UE中的TQueues可以跨线程安全使用
根据我上面的了解,我目前的设计在逻辑上似乎是不可能的。
到目前为止,这些是我的替代方案:
使用两个队列
一个在后台线程上出列和评分
另一个通过 tick
将游戏线程上的结果出列
使用游戏线程中存在的委托,通过 tick
调用 OccurrenceEvaluated.Broadcast()- 对结果进行评分后,将 Occurrence.GetOwner()->PurposeSelected(Occurrence) 绑定到 OccurrenceEvaluated
我已经看到 c++ 使用称为 Future() 的东西(或类似的东西)来执行 ASync 任务,而且 UE 似乎与 TFuture<> && TFuture.IsReady( ),但我还没有深入研究它以及它如何 returns 数据
- 与 FAsyncTask 相同
我犹豫要不要实施任何利用 tick 来检查数据是否 updated/returned 来自后台线程的设计。
任何人都可以建议相关的设计实践,或者阐明 return 从后台线程执行到主线程的性质(我很难找到 research/info 的正确问题关于这个)?
早上好,
线程可以帮助您完成工作 faster/more 响应迅速但有很多陷阱。在这种情况下的简短回答我使用这些结构
#include <future>
#include <thread>
#include <vector>
int main()
{
// make data shared
auto data = std::make_shared<std::vector<int>>();
data -> push_back(42);
// future will be a future<int> deduced from return from vector
// async will run lambda in other thread
// capture data by value! It copies the shared_ptr and will
// increase its reference count.
auto future = std::async(std::launch::async, [data]
{
auto value = data[0];
return value;
// shared_ptr data goes out of scope and reference count to data is decreased
});
// synchronize with thread and get "calculated" value
auto my_value = future.get();
// now data on the main thread goes out of scope reference count is decreased. Only when both the thread are done AND this function exits then data is deleted.
return 0;
}
- 大多数对象都会占用内存,并且在分配期间内存不会在 1 个时钟周期内更新。您可以使用 sdt::mutex 和 std::scoped_lock 来保护内存。尝试寻找那些。
- 你经常需要在线程之间同步information/processing,看看std::condition_variable(但是他们有坑https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables)
- C++ 对 类、std::thread 和 std::async/std::future 有很好的支持。我个人喜欢 std::future 的解决方案,因为你可以 return 值和异常 (!) 当您调用 future.get() 时来自其他线程
- 了解 lambda 和捕获,它们对于与 std::thread/std::async 一起使用至关重要
- 对象的生命周期!在线程之间共享对象时,您必须确保一个线程不会删除其他线程正在使用的对象。使用线程时不要使用原始指针 and/or new/delete。我个人经常在线程之间共享信息时使用 std::make_shared/std::shared_ptr 到 data/objects。
- 另一个棘手的事情是,有时您无法确定另一个线程中的工作在创建后是否已经开始。例如。当 std::async returns 一个线程被创建但它不能保证真正开始(操作系统调度等...)。如果您想真正确定它已经启动,那么在异步调用 returns 之后,您将不得不等待您在线程函数开始时设置的 condition_variable。
我希望这些评论可以帮助您入门。
我找到了一个完美的解决方案。由于这些事件对时间不是特别敏感,我只是使用 Unreal Engine 的 AsyncTask() 从我的后台线程在游戏线程上安排一个异步任务。正如@Pepjin Kramer 指出的那样,与 std::async.
相同这么简单,基本就是一记耳光