在开发过程中如果把所有的逻辑都放到GameThread里,难免会卡顿。因此这时候,往往会用到多线程。UE4提供的多线程解决方案有三种。这里我们着重说一下AsyncTask的应用。
一般来说,用AsyncTask都是一些业务逻辑不复杂的交给它来处理。因为省去了new Thread的开销。
AsyncTask 下边又有几种不同的Task类型,这里说的是FNonAbandonableTask 和 FAutoDeleteAsyncTask。
对于FAutoDeleteAsyncTask,在任务结束的时候,它会自动清理。我们并不需要手动清理,但是造成的问题就是,如果我们在new FAutoDeleAsyncTask 传入了外部的类实例,在外部类实例销毁时,new 出来的Task 可能并没有运行结束,这里就会报空指针的问题,造成程序的中断和崩溃。
因此,当我们需要外部传入类实例的时候,可以使用FAutoDeleteAsyncTask,因为它下边提供了可以用来判断任务是否已经结束了的API接口。
如下:
/** Returns true if the work and TASK has completed, false while it's still in progress.
* prior to returning true, it synchronizes so the task can be destroyed or reused
*/
bool IsDone()
{
if (!IsWorkDone())
{
return false;
}
SyncCompletion();
return true;
}
/** Returns true if the work has completed, false while it's still in progress.
* This does not block and if true, you can use the results.
* But you can't destroy or reuse the task without IsDone() being true or EnsureCompletion()
*/
bool IsWorkDone() const
{
if (WorkNotFinishedCounter.GetValue())
{
return false;
}
return true;
}
/** Returns true if the work has not been started or has been completed.
* NOT to be used for synchronization, but great for check()'s
*/
bool IsIdle() const
{
return WorkNotFinishedCounter.GetValue() == 0 && QueuedPool == 0;
}
/**
* Wait until the job is complete
* @param bDoWorkOnThisThreadIfNotStarted if true and the work has not been started, retract the async task and do it now on this thread
**/
void EnsureCompletion(bool bDoWorkOnThisThreadIfNotStarted = true)
{
bool DoSyncCompletion = true;
if (bDoWorkOnThisThreadIfNotStarted)
{
if (QueuedPool)
{
if (QueuedPool->RetractQueuedWork(this))
{
// we got the job back, so do the work now and no need to synchronize
DoSyncCompletion = false;
DoWork();
FinishThreadedWork();
QueuedPool = 0;
}
}
else if (WorkNotFinishedCounter.GetValue()) // in the synchronous case, if we haven't done it yet, do it now
{
DoWork();
}
}
if (DoSyncCompletion)
{
SyncCompletion();
}
CheckIdle(); // Must have had bDoWorkOnThisThreadIfNotStarted == false and needed it to be true for a synchronous job
}
因此,可以在析构一个类时,等待所有的Task完成,再执行析构和退出,就不会再报异常错误。
// 数组
TArray<FAsyncTask<DoKafkaTask>*> TaskArr;
FAsyncTask<DoKafkaTask>* task = new FAsyncTask<DoKafkaTask>(this, request);
TaskArr.Add(task);
task->StartBackgroundTask();
AKafkaMsgManager::~AKafkaMsgManager()
{
// 确保所有的异步任务被执行完毕 不然会在异步任务中报空指针的错误
for (auto local_task : TaskArr)
{
if (!local_task->IsDone())
{
local_task->EnsureCompletion();
}
}
}
其他另外关于多线程和异步的文章,请参阅。
UE4_异步_数据处理(Json)
UE4_多线程、异步执行任务