Вы можете использовать словари для этого. Словари - это хранилища ключей и ценностей.
>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2
Вы можете использовать имена переменных ключей для достижения эффекта переменных переменных без риска для безопасности.
>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'
В тех случаях, когда вы думаете сделать что-то вроде
var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...
список может быть более подходящим, чем dict. Список представляет упорядоченную последовательность объектов с целыми индексами:
l = ['foo', 'bar', 'baz']
print(l[1]) # prints bar, because indices start at 0
l.append('potatoes') # l is now ['foo', 'bar', 'baz', 'potatoes']
Для упорядоченных последовательностей списки удобнее, чем dict с целыми ключами, потому что списки поддерживают итерацию в порядке индекса, slicing , append
и другие операции, которые потребуют неудобного управления ключами с помощью dict.
Небольшая заметка - этот подход:
Task<Customer> task = GetCustomers();
task.Wait()
работает для WinRT.
Позвольте мне объяснить:
private void TestMethod()
{
Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
task.Wait(); // wait executing the method
var customer = task.Result; // get's result.
Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
public Customer()
{
new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
}
public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
return Task.Run(() => new Customer
{
Name = "MyName"
});
}
Кроме того, этот подход работает только для решений для Windows Store!
Примечание. Этот способ не является потокобезопасным, если вы вызываете свой метод внутри другого метода асинхронизации (согласно комментариям @Servy)
Это хорошо работает для меня
public static class TaskHelper
{
public static void RunTaskSynchronously(this Task t)
{
var task = Task.Run(async () => await t);
task.Wait();
}
public static T RunTaskSynchronously<T>(this Task<T> t)
{
T res = default(T);
var task = Task.Run(async () => res = await t);
task.Wait();
return res;
}
}
MyAsyncMethod().RunTaskSynchronously();
– ygoe
26 September 2016 в 15:20
Если я правильно читаю ваш вопрос - код, который хочет, чтобы синхронный вызов метода асинхронизации выполнялся в приостановленном потоке диспетчера. И вы хотите фактически синхронизировать этот поток до тех пор, пока не будет завершен метод async.
Асинхронные методы на C # 5 приводятся в действие путем эффективного измельчения метода на куски под капотом и возвращения Task
, который может отслеживать общее завершение всего шабанга. Однако, как выполняются методы прерывания, может зависеть от типа выражения, переданного оператору await
.
В большинстве случаев вы будете использовать await
для выражения типа Task
. Выполнение задачи await
шаблона является «умным», поскольку оно отбрасывает SynchronizationContext
, что в основном приводит к следующему:
await
, включен поток потока сообщений диспетчера или WinForms, он гарантирует, что куски асинхронного метода выполняются как часть обработки очереди сообщений. await
, находится в потоке пула потоков, то оставшиеся куски асинхронного метода встречаются в любом месте пула потоков. Вот почему вы, вероятно, сталкиваетесь с проблемами - реализация метода асинхронного тестирования пытается запустить остальные на диспетчере - даже если он приостановлен.
.... резервное копирование! ....
Я должен задать вопрос, , почему вы пытаетесь синхронно блокировать метод async? Это может привести к тому, что метод будет вызван асинхронно. В общем случае, когда вы начинаете использовать await
в методе Dispatcher или UI, вам нужно будет перевернуть весь ваш асинхронный поток пользовательского интерфейса. Например, если ваш столбец был примерно таким:
Затем, как только код был преобразован для использования async, вы, как правило, получите
Фактически Ответ
Класс AsyncHelpers выше работает, потому что он ведет себя как вложенный цикл сообщений, но он устанавливает свой собственный параллельный механизм для диспетчера, вместо того, чтобы пытаться выполнить сам Диспетчер. Это один из способов решения вашей проблемы.
Другим обходным решением является выполнение вашего асинхронного метода в потоке threadpool, а затем дождаться его завершения. Делать это легко - вы можете сделать это со следующим фрагментом:
var customerList = TaskEx.RunEx(GetCustomers).Result;
Конечным API будет Task.Run (...), но с CTP вам понадобятся суффиксы Ex ( здесь ).
TaskEx.RunEx(GetCustomers).Result
зависает приложение, когда оно запускается в приостановленном потоке диспетчера. Кроме того, метод GetCustomers () обычно запускается async, однако в одной ситуации он должен запускаться синхронно, поэтому я искал способ сделать это, не создавая версию метода синхронизации.
– Rachel
25 February 2011 в 15:39
async
; конечно, следует избегать вложенных циклов.
– Stephen Cleary
5 September 2013 в 16:59
Main()
не компилируется; в какой-то момент вы получили i>, чтобы преодолеть разрыв между мирами синхронизации и асинхронного программирования. Это не очень редкая ситуация с [i0] очень " i>, это необходимо буквально каждой программе, вызывающей метод async. Нет возможности не выполнять & quot; делать sync-over-async & quot; i>, просто вариант для того, чтобы отключить эту нагрузку до вызывающего метода вместо того, чтобы занять его в том, который вы сейчас пишете.
– Mark Amery
13 June 2017 в 13:20
async
на все методы в моем приложении. И это много. Разве это не может быть по умолчанию?
– ygoe
13 December 2017 в 17:53
Этот ответ предназначен для всех, кто использует WPF для .NET 4.5.
Если вы попытаетесь выполнить Task.Run()
в потоке GUI, тогда task.Wait()
будет вешать бесконечно, если вы не имеют ключевое слово async
в определении вашей функции.
Этот метод расширения решает проблему, проверяя, есть ли мы в потоке графического интерфейса пользователя, и если да, выполняем задачу в потоке диспетчера WPF.
Этот класс может действовать как клей между миром async / await и неасинхронным / ожидающим миром в ситуациях, когда это неизбежно, например свойства MVVM или зависимости от других API, которые не используют async / ожидают.
/// <summary>
/// Intent: runs an async/await task synchronously. Designed for use with WPF.
/// Normally, under WPF, if task.Wait() is executed on the GUI thread without async
/// in the function signature, it will hang with a threading deadlock, this class
/// solves that problem.
/// </summary>
public static class TaskHelper
{
public static void MyRunTaskSynchronously(this Task task)
{
if (MyIfWpfDispatcherThread)
{
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E99213. Task did not run to completion.");
}
}
else
{
task.Wait();
if (task.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E33213. Task did not run to completion.");
}
}
}
public static T MyRunTaskSynchronously<T>(this Task<T> task)
{
if (MyIfWpfDispatcherThread)
{
T res = default(T);
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E89213. Task did not run to completion.");
}
return res;
}
else
{
T res = default(T);
var result = Task.Run(async () => res = await task);
result.Wait();
if (result.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E12823. Task did not run to completion.");
}
return res;
}
}
/// <summary>
/// If the task is running on the WPF dispatcher thread.
/// </summary>
public static bool MyIfWpfDispatcherThread
{
get
{
return Application.Current.Dispatcher.CheckAccess();
}
}
}
В вашем коде ваше первое ожидание выполнения задачи, но вы не запустили ее, так что она ждет бесконечно. Попробуйте следующее:
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Изменить:
Вы говорите, что получаете исключение. Пожалуйста, напишите более подробную информацию, включая трассировку стека. Mono содержит следующий тестовый пример:
[Test]
public void ExecuteSynchronouslyTest ()
{
var val = 0;
Task t = new Task (() => { Thread.Sleep (100); val = 1; });
t.RunSynchronously ();
Assert.AreEqual (1, val);
}
Проверьте, работает ли это для вас. Если это не так, но очень маловероятно, у вас может быть некоторая нечетная сборка Async CTP. Если это сработает, вам может потребоваться изучить, что именно генерирует компилятор, и как Task
экземпляр отличается от этого образца.
Редактировать # 2:
Я проверил с Reflector, что описанное вами исключение возникает, когда m_action
- null
. Это странно, но я не эксперт в Async CTP. Как я уже сказал, вы должны декомпилировать свой код и посмотреть, как именно создается Task
, каким образом его m_action
является null
.
P.S. В чем дело с случайными сокращениями? Учиться разрабатывать?
RunSynchronously may not be called on a task unbound to a delegate
. Google не помогает, поскольку все результаты для китайского ...
– Rachel
23 February 2011 в 19:51
await
. Исключение, опубликованное в моем предыдущем комментарии, является исключением, которое я получаю, хотя это одно из немногих, что я не могу Google и найти причину или разрешение.
– Rachel
23 February 2011 в 20:40
async
и async
ключевые слова - не что иное, как синтаксический сахар. Компилятор создает код для создания Task<Customer>
в GetCustomers()
, так что я бы посмотрел первым. Что касается исключения, вы отправили только сообщение исключения, которое бесполезно без типа исключения и трассировки стека. Вызовите метод исключения ToString()
и пост-вывод в вопросе.
– Dan Abramov
23 February 2011 в 20:44
Это работает для меня
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
public static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static void RunSync(Func<Task> func)
{
_myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
class SomeClass
{
public async Task<object> LoginAsync(object loginInfo)
{
return await Task.FromResult(0);
}
public object Login(object loginInfo)
{
return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
//return this.LoginAsync(loginInfo).Result.Content;
}
}
class Program
{
static void Main(string[] args)
{
var someClass = new SomeClass();
Console.WriteLine(someClass.Login(1));
Console.ReadLine();
}
}
}
Почему бы не создать такой вызов, как:
Service.GetCustomers();
, который не является асинхронным.
Я думаю, что следующий вспомогательный метод также может решить проблему.
private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
{
TResult result = default(TResult);
var autoResetEvent = new AutoResetEvent(false);
Task.Run(async () =>
{
try
{
result = await func();
}
catch (Exception exc)
{
mErrorLogger.LogError(exc.ToString());
}
finally
{
autoResetEvent.Set();
}
});
autoResetEvent.WaitOne();
return result;
}
Может использоваться следующим образом:
InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
Я столкнулся с этим несколько раз, в основном в модульном тестировании или в разработке служб Windows. В настоящее время я всегда использую эту функцию:
var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
{
Trace.WriteLine("Task runSync Start");
await TaskEx.Delay(2000); // Simulates a method that returns a task and
// inside it is possible that there
// async keywords or anothers tasks
Trace.WriteLine("Task runSync Completed");
})).Unwrap();
Trace.WriteLine("Before runSync Wait");
runSync.Wait();
Trace.WriteLine("After runSync Waited");
Это просто, легко, и у меня не было проблем.
Удивленный никто не упомянул об этом:
public Task<int> BlahAsync()
{
// ...
}
int result = BlahAsync().GetAwaiter().GetResult();
Не так хорошо, как некоторые из других методов здесь, но он имеет следующие преимущества:
AggregateException
(например Result
) Task
, так и для Task<T>
( попробуйте сами! ) Кроме того, поскольку GetAwaiter
утиный, это должно работать для любого объекта, который возвращается из async (например, ConfiguredAwaitable
или YieldAwaitable
), а не только Задачи.
edit: Обратите внимание, что этот подход (или использование .Result
) возможен в тупик, если вы не убедитесь добавлять .ConfigureAwait(false)
каждый раз, когда вы ожидаете, для всех асинхронных методов, которые могут быть достигнуты с BlahAsync()
(а не только с теми, которые он вызывает напрямую). Объяснение .
// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
// all its descendants use ConfigureAwait(false)
// too. Then you can be sure that
// BlahAsync().GetAwaiter().GetResult()
// won't deadlock.
Если вы слишком ленивы, чтобы добавить .ConfigureAwait(false)
повсюду, и вы не заботитесь о производительности, вы также можете сделать
Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
Вы можете использовать CoRoutines . См. Реализацию Caliburn.Micro . У меня есть пользовательская реализация здесь .
private int GetSync()
{
try
{
ManualResetEvent mre = new ManualResetEvent(false);
int result = null;
Parallel.Invoke(async () =>
{
result = await SomeCalcAsync(5+5);
mre.Set();
});
mre.WaitOne();
return result;
}
catch (Exception)
{
return null;
}
}
использовать ниже код snip
Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
Попробуйте использовать следующий код для меня:
public async void TaskSearchOnTaskList (SearchModel searchModel)
{
try
{
List<EventsTasksModel> taskSearchList = await Task.Run(
() => MakeasyncSearchRequest(searchModel),
cancelTaskSearchToken.Token);
if (cancelTaskSearchToken.IsCancellationRequested
|| string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
{
return;
}
if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
{
RunOnUiThread(() => {
textViewNoMembers.Visibility = ViewStates.Visible;
taskListView.Visibility = ViewStates.Gone;
});
taskSearchRecureList = null;
return;
}
else
{
taskSearchRecureList = TaskFooterServiceLayer
.GetRecurringEvent(taskSearchList);
this.SetOnAdapter(taskSearchRecureList);
}
}
catch (Exception ex)
{
Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
}
}
Гораздо проще запустить задачу в пуле потоков, а не пытаться обмануть планировщика для его синхронного запуска. Таким образом, вы можете быть уверены, что это не затормозит. Эффект зависит от контекстного переключателя.
Task<MyResult> DoSomethingAsync() { ... }
// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());
// Will block until the task is completed...
MyResult result = task.Result;
Task.Run(DoSomethingAsync)
? Это удаляет один уровень делегатов.
– ygoe
13 December 2017 в 17:36
Просто вызов .Result;
или .Wait()
- это риск блокировок, о котором многие говорили в комментариях. Поскольку большинство из нас, как oneliners, вы можете использовать их для .Net 4.5<
Приобретение значения с помощью метода async:
var result = Task.Run(() => asyncGetValue()).Result;
Синхронно вызывать метод асинхронизации
Task.Run(() => asyncMethod()).Wait();
Никаких проблем с блокировкой не произойдет из-за использования Task.Run
.
Источник:
Самый простой способ, который я нашел для запуска задачи синхронно и без блокировки потока пользовательского интерфейса, - использовать RunSynchronously () как:
Task t = new Task(() =>
{
//.... YOUR CODE ....
});
t.RunSynchronously();
В моем случае у меня есть событие, которое срабатывает, когда что-то происходит. Я не знаю, сколько раз это произойдет. Таким образом, я использую код выше в моем случае, поэтому всякий раз, когда он срабатывает, он создает задачу. Задачи выполняются синхронно, и это отлично работает для меня. Я был просто удивлен, что мне потребовалось столько времени, чтобы выяснить, насколько это просто. Обычно рекомендации намного сложнее и подвержены ошибкам. Это было просто и чисто.
Рекомендовать этому ответу три года. Я написал его, основываясь главным образом на опыте с .Net 4.0 и очень мало с 4.5, особенно с async-await
. Вообще говоря, это приятное простое решение, но иногда это ломает ситуацию. Пожалуйста, прочитайте обсуждение в комментариях.
Просто используйте это:
// For Task<T>: will block until the task is completed...
var result = task.Result;
// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();
См.: TaskAwaiter , Task.Result , Task.RunSynchronously
Используйте это:
var x = (IAsyncResult)task;
task.Start();
x.AsyncWaitHandle.WaitOne();
... или это:
task.Start();
task.Wait();
Result
может легко вызвать тупик в коде async
, как я описываю в своем блоге.
– Stephen Cleary
5 September 2013 в 16:58
DynamicNodeProviderBase
, нельзя объявить его как метод async
. Либо мне пришлось заменить новую библиотеку, либо просто вызвать синхронный режим.
– justin.lovell
23 January 2014 в 08:56
AspNetSynchronizationContext
, поэтому этот конкретный взлом не будет работать, если вы вызываете эти API.
– Stephen Cleary
23 January 2014 в 14:33
Или вы могли бы просто пойти с:
customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;
Для этого для компиляции убедитесь, что вы ссылаетесь на сборку:
System.Net.Http.Formatting
В wp8:
Оберните его:
Task GetCustomersSynchronously()
{
Task t = new Task(async () =>
{
myCustomers = await GetCustomers();
}
t.RunSynchronously();
}
Вызовите его:
GetCustomersSynchronously();
Я нашел этот код в компоненте Microsoft.AspNet.Identity.Core, и он работает.
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None, TaskCreationOptions.None,
TaskContinuationOptions.None, TaskScheduler.Default);
// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap<TResult>().GetAwaiter().GetResult();
}
CancellationToken
для моего решения. – RredCat 16 December 2013 в 23:41