Я хотел бы выполнить некоторый код в фоновом потоке и периодически использовать код фона в основном потоке. Что-то вроде следующего:
void functionThatMustBeRunOnMainThread(string arg) {
Debug.Print("On main thread: "+arg);
}
// Run the task asynchronously. On completion, call completionAction in the main thread.
void launchAsyncBackgroundTask( Action<bool> completionAction ) {
performInArbitraryBackgroundThread(() => {
// do first long running thing
sleep(10);
// notify main thread
performOnMainThread(() => {
functionThatMustBeRunOnMainThread("phase 1");
});
// do second long running thing
sleep(10);
// notify main thread
performOnMainThread(() => {
functionThatMustBeRunOnMainThread("phase 2");
});
// do final long running thing
sleep(10);
performOnMainThread(() => {
Debug.Print("Done!");
completionAction(true);
});
});
}
Я знаю о BackgroundWorker
но он не предлагает гибкости, которую я ищу.
Здесь два пункта -
Это обычное дело в Obj-C с помощью Grand Central Dispatch (это работает в основном так, как описано выше). Есть ли эквивалент С#?
Вы можете легко достичь того, что вам нужно, с помощью async-await
вместе с Task Parallel Library
:
В этом примере предполагается, что MethodThatDoesStuffInBackground
или любой из других трудоемких методов - это операции с ЦП. Если нет, и они делают IO, вы можете отказаться от использования Task.Run
:
(Этот метод должен быть вызван из потока пользовательского интерфейса для правильной работы)
public async Task DoStuff()
{
await Task.Run(() => MethodThatDoesStuffInBackground());
FunctionThatMustRunOnMainThread();
await Task.Run(() => MethodThatDoesMoreStuffInBackground());
FunctionThatMustRunOnMainThread();
await Task.Run(() => EvenMoreWorkInBackgroundThread());
FunctionThatMustRunOnMainThread();
}
DoStuff
из потока пользовательского интерфейса.
Task.Delay
для асинхронных задержек, которые не блокируют потоки, возможно, Progress<T>
для сигнализации между потоками, используя сложные объекты событий
Я рекомендую использовать Task.Run
для фоновой работы, IProgress<T>
для обновления прогресса и Task
для уведомления о завершении. Такой подход позволяет сохранить фоновую логику в одном месте, отдельно от пользовательского интерфейса.
Что-то вроде этого:
// Run the task asynchronously. On completion, call completionAction in the main thread.
async Task launchBackgroundTaskAsync( Action<bool> completionAction ) {
var progress = new Progress<string>(arg => {
Debug.Print("On main thread: "+arg);
};
await Task.Run(() => BackgroundLogic(progress));
completionAction(true);
}
void BackgroundLogic(IProgress<string> progress) {
// do first long running thing
sleep(10);
// notify main thread
if (progress != null)
progress.Report("phase 1");
// do second long running thing
sleep(10);
// notify main thread
if (progress != null)
progress.Report("phase 2");
// do final long running thing
sleep(10);
if (progress != null)
progress.Report("Done!");
}
Обратите внимание, что completionAction
больше не надо, так как launchBackgroundTaskAsync
сама возвращает Task
. Это может быть просто удалено без потери возможности - просто звонящие этого использование метода await
:
async Task launchBackgroundTaskAsync() {
var progress = new Progress<string>(arg => {
Debug.Print("On main thread: "+arg);
};
await Task.Run(() => BackgroundLogic(progress));
}
Progress
? то есть я должен рассматривать Progress
как синоним для performOnMainThread
? Вызывается ли он синхронно с точки зрения потока BG (т. Е. Фоновый поток блокируется до тех пор, пока progress.Repert()
возвращается), или async (операция фонового потока продолжается, пока progress.Report()
выполняет async в основном потоке)?
Progress<T>
принимает делегат Action<T>
, поэтому вы можете поместить в него любой код. Он работает асинхронно , поэтому progress.Report
не будет блокировать фоновый поток.
await Task.Run()
.