Ответ 1
private void RunAsync()
{
string param = "Hi";
Task.Run(() => MethodWithParameter(param));
}
private void MethodWithParameter(string param)
{
//Do stuff
}
Edit
Из-за большого спроса я должен отметить, что запущенный Task
будет работать параллельно с вызывающим потоком. Предполагая, что по умолчанию TaskScheduler
это будет использовать .NET ThreadPool
. В любом случае, это означает, что вам нужно учитывать любые параметры, передаваемые в Task
, как потенциально доступные несколькими потоками одновременно, что делает их общим. Это включает в себя доступ к ним в вызывающем потоке.
В моем вышеприведенном коде этот случай сделан полностью спорным. Строки неизменяемы. Вот почему я использовал их в качестве примера. Но скажите, что вы не используете String
...
Одним из решений является использование async
и await
. Это по умолчанию будет захватывать SynchronizationContext
вызывающего потока и будет создавать продолжение для остальной части метода после вызова await
и прикрепить его к созданному Task
. Если этот метод работает в потоке GUI WinForms, он будет иметь тип WindowsFormsSynchronizationContext
.
Продолжение будет выполняться после того, как оно будет отправлено обратно в захваченный SynchronizationContext
- снова только по умолчанию. Таким образом, вы вернетесь к потоку, который вы начали после вызова await
. Вы можете изменить это различными способами, особенно используя ConfigureAwait
. Короче говоря, остальная часть этого метода не будет продолжаться до тех пор, пока после Task
не завершится в другом потоке. Но вызывающий поток будет продолжать работать параллельно, но не весь метод.
Ожидание завершения остальной части метода может быть нежелательным. Если ничто в этом методе не получит доступ к параметрам, переданным в Task
, вы можете вообще не использовать await
.
Или, может быть, вы используете эти параметры намного позже в методе. Нет причин для await
немедленно, так как вы можете продолжать безопасно выполнять работу. Помните, что вы можете сохранить Task
, возвращенный в переменной, и await
на нем позже - даже в том же методе. Например, как только вам нужно безопасно получить доступ к переданным параметрам после выполнения какой-либо другой работы. Опять же, вам не нужно await
в Task
при запуске.
В любом случае простой способ сделать этот поток безопасным по отношению к параметрам, переданным в Task.Run
, состоит в том, чтобы сделать это:
Вы должны сначала украсить RunAsync
async
:
private async void RunAsync()
Важное примечание
Предпочтительно метод, отмеченный async
, не должен возвращать void, как упоминается связанная документация. Общим исключением для этого являются обработчики событий, такие как нажатия кнопок и т.д. Они должны вернуть пустоту. В противном случае я всегда пытаюсь вернуть Task
или Task<TResult>
при использовании async
. Это хорошая практика по нескольким причинам.
Теперь вы можете await
запустить Task
, как показано ниже. Вы не можете использовать await
без async
.
await Task.Run(() => MethodWithParameter(param));
//Code here and below in the same method will not run until AFTER the above task has completed in one fashion or another
Итак, в общем случае, если вы await
задаете задачу, которую вы можете избежать, обрабатывать переданные в параметрах как потенциально разделяемый ресурс со всеми ошибками изменения чего-то из нескольких потоков одновременно. Кроме того, остерегайтесь closures. Я не буду об этом подробно рассказывать, но связанная статья отлично справляется с этим.
Боковое примечание
Отключить тему, но будьте осторожны с использованием любого типа "блокировки" в потоке GUI WinForms из-за того, что он помечен [STAThread]
. Использование await
вообще не будет блокировать, но иногда я вижу, что оно используется в сочетании с какой-то блокировкой.
"Блок" находится в кавычках, потому что вы технически не можете заблокировать поток графического интерфейса WinForms. Да, если вы используете lock
в потоке GUI WinForms, он все равно будет накачивать сообщения, несмотря на то, что вы думаете, что это "заблокировано". Это не так.
Это может вызвать странные проблемы в очень редких случаях. Одна из причин, по которой вы никогда не хотите использовать lock
при рисовании, например. Но это крайний и сложный случай; однако я видел, что это вызывает сумасшедшие проблемы. Поэтому я отметил это ради полноты.