private void RunAsync()
{
string param = "Hi";
Task.Run(() => MethodWithParameter(param));
}
private void MethodWithParameter(string param)
{
}
Редагувати
Через популярний попит я повинен зазначити, що Task
запущений буде працювати паралельно із потоком виклику. Якщо припустити, що за замовчуванням TaskScheduler
це буде використовувати .NET ThreadPool
. У будь-якому випадку, це означає, що вам потрібно враховувати, які параметри (параметри) передаються до них Task
як потенційно доступні декільком потокам одночасно, роблячи їх спільним станом. Це включає доступ до них у потоці виклику.
У моєму вище коді цей випадок зроблений повністю спірним. Струни незмінні. Тому я взяв їх як приклад. Але скажімо, ви не використовуєте String
...
Одним із рішень є використання async
та await
. Це за замовчуванням захопить SynchronizationContext
виклик потоку і створить продовження для решти методу після виклику await
та приєднає його до створеного Task
. Якщо цей метод запущений у графічному інтерфейсі WinForms, він буде типу WindowsFormsSynchronizationContext
.
Продовження триватиме після опублікування назад до захопленого SynchronizationContext
- знову лише за замовчуванням. Тож ви повернетесь до теми, з якої почали після await
дзвінка. Ви можете змінити це різними способами, зокрема, використовуючи ConfigureAwait
. Коротше кажучи, інша частина цього методу не буде тривати до тих пір , післяTask
завершення іншого потоку. Але викличний потік буде продовжувати працювати паралельно, тільки не решта методу.
Це очікування завершення запуску решти методу може бути або не бути бажаним. Якщо пізніше в цьому методі нічого не доступно до параметрів, переданих Task
вам, ви можете взагалі не захотіти використовувати await
.
Або, можливо, ви використовуєте ці параметри значно пізніше в методі. Немає причин await
негайно, оскільки ви могли б продовжувати безпечно виконувати роботу. Пам'ятайте, ви можете зберігати Task
повернене у змінній, а await
потім - навіть у тому ж методі. Наприклад, як тільки вам потрібно буде безпечно отримати доступ до переданих параметрів, після виконання декількох інших робіт. Знову ж , вам НЕ потрібно await
на Task
праві , коли ви запустите його.
У будь-якому випадку, простий спосіб зробити цей потік безпечним щодо переданих параметрів Task.Run
- це зробити це:
Ви повинні спочатку прикрасити RunAsync
з async
:
private async void RunAsync()
Важлива примітка
Переважно, щоб метод, позначений, не повертав недійсним, як згадується у пов'язаній документації. Загальним винятком з цього є обробники подій, такі як клацання кнопок тощо. Вони повинні повернутися недійсними. В іншому випадку я завжди намагаюся повернути a або при використанні . Це хороша практика з багатьох причин.async
Task
Task<TResult>
async
Тепер ви можете await
запустити, Task
як показано нижче. Ви не можете використовувати await
без async
.
await Task.Run(() => MethodWithParameter(param));
Отже, загалом, якщо ви ставите await
завдання, ви можете уникнути розгляду переданих параметрів як потенційно спільного ресурсу з усіма підводними камінами модифікації чогось із декількох потоків одночасно. Крім того, остерігайтеся закриття . Я не буду їх детально висвітлювати, але пов'язана стаття це чудово справляє.
Бічна примітка
Трохи не в темі, але будьте обережні, використовуючи будь-який тип "блокування" в графічному інтерфейсі WinForms через те, що він позначений [STAThread]
. Використання await
взагалі не блокує, але іноді я бачу, що воно використовується разом із якимсь блокуванням.
"Блок" в лапках, оскільки технічно ви не можете заблокувати графічний інтерфейс WinForms . Так, якщо ви використовуєте lock
графічний інтерфейс WinForms, він все одно буде перекачувати повідомлення, незважаючи на те, що ви думаєте, що він "заблокований". Це не.
Це може викликати дивні проблеми у дуже рідкісних випадках. lock
Наприклад, одна з причин того, що ви ніколи не хочете використовувати, коли малюєте. Але це надзвичайно складний випадок; однак я бачив, що це спричиняє божевільні проблеми. Тож я це зазначив заради повноти.