我有一个异步方法,当我需要更新UI控件内容时会调用该方法,如下所示:
public async Task UpdateUI(int i)
{
Debug.WriteLine("Enter {0}", i);
DoSomethingSync(1000);
Debug.WriteLine("Await {0}", i);
await GetDataFromServerAsync(5000);
//Update UI Controls
Debug.WriteLine("Update {0}", i);
DoSomethingSync(2000);
Debug.WriteLine("Exit {0}", i);
}
public void DoSomethingSync(int delay)
{
Thread.Sleep(delay);
}
public Task GetDataFromServerAsync(int delay)
{
return TaskEx.Delay(delay);
}当服务器上的数据发生更改时,我会收到来自不同线程的通知,并且我必须调用UI线程上的UpdateUI方法,如下所示:
public async void OnServerDataChanged(int i)
{
Debug.WriteLine("WaitOne {0}", i);
_semaphore.WaitOne();
try
{
await Task.Factory.StartNew(async () =>
{
await UpdateUI(i);
}, CancellationToken.None, TaskCreationOptions.None, _scheduler);
}
finally
{
_semaphore.Release();
Debug.WriteLine("Release {0}", i);
}
}我模拟多线程通知:
public void SimulateMultiThreadingNotification()
{
TaskEx.Run(() =>
{
for (var i = 0; i < 3; i++)
{
TaskEx.Run(() =>
{
var id = Thread.CurrentThread.ManagedThreadId;
OnServerDataChanged(id);
});
}
});
}运行:
_semaphore = new Semaphore(1, 1);
_scheduler = TaskScheduler.FromCurrentSynchronizationContext();
SimulateMultiThreadingNotification();输出:
Enter 11
Await 11
Release 11
Enter 12
Await 12
Release 12
Enter 6
Await 6
Release 6
Update 11
Exit 11
Update 12
Exit 12
Update 6
Exit 6如何同步以使方法按如下方式顺序执行:
Enter 11
Await 11
Update 11
Exit 11
Release 11
Enter 12
Await 12
Update 12
Exit 12
Release 12
Enter 6
Await 6
Update 6
Exit 6
Release 6比x前进!
编辑:我找到了解决方案:
public void OnServerDataChanged(int i)
{
_semaphore.WaitOne();
try
{
var tcs = new TaskCompletionSource<bool>();
Task.Factory.StartNew(async () =>
{
await UpdateUI(i);
tcs.SetResult(true);
}, CancellationToken.None, TaskCreationOptions.None, _scheduler);
tcs.Task.Wait();
}
finally
{
_semaphore.Release();
Debug.WriteLine("Release {0}", i);
}
}编辑2: Yuval Itzchakov的解决方案:
public void OnServerDataChanged(int i)
{
_semaphore.WaitOne();
try
{
Task.Factory.StartNew(async () =>
{
await UpdateUI(i);
}, CancellationToken.None, TaskCreationOptions.None, _scheduler).Unwrap().Wait();
}
finally
{
_semaphore.Release();
Debug.WriteLine("Release {0}", i);
}
}发布于 2015-06-26 02:50:49
您的问题在于Task.Factory.StartNew没有正确地“处理”返回lambda的async Task,因为它们会生成一个Task<Task>,而实际上是在外部await上执行,而不是在内部Task上执行。
你可以用SemaphoreSlim代替Semaphore,它有一个你可以异步等待的WaitAsync。
调用Unwrap()可以解决您的问题:
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
public async Task ServerDataChangedAsync(int i)
{
Debug.WriteLine("WaitAsync {0}", i);
await _semaphore.WaitAsync();
try
{
await Task.Factory.StartNew(async () =>
{
await UpdateUI(i);
}, CancellationToken.None, TaskCreationOptions.None, _scheduler).Unwrap();
}
finally
{
_semaphore.Release();
Debug.WriteLine("Release {0}", i);
}
}另外,不要使用async void,这是针对事件处理程序的。改为使用async Task。
https://stackoverflow.com/questions/31049705
复制相似问题