1

我想知道这是否可以在不使用协程的情况下完成:

private void GetAll()
{
    if (_refreshA)
    {
        collA = new ObservableCollection(GetA<A>());
        _refreshA = false;
    }

    if (_refreshB)
    {
        collB = new ObservableCollection(GetB<B>(param))
        _refreshB = false;
    }

    if (_refreshC)
    {
        collC = new ObservableCollection(GetC<C>())
        _refreshC = false;
    }
}

CollA、CollB 和 CollC 用于 UI 线程。我需要 GetAll 确实在与 UI 不同的线程上执行,并且我需要 GetA()、GetB(param) 和 GetC() 一个接一个地执行(而不是并行执行)。

结果应该是(如果所有 3 _refreshX 都为真):

create new thread
execute GetA() on new thread
wait for data to arrive
update UI collection with new data
create new thread
execute GetB(param) on new thread
wait for data to arrive
update UI collection with new data
create new thread
execute GetC() on new thread
wait for data to arrive
update UI collection with new data

这只能使用 TPL 完成,还是我也需要使用协程?

编辑:因为我有一个错误的印象是异步等待不能在 .NET 4 上使用,svick 和亚当罗宾逊向我指出,我将尝试使用异步等待来实现这一点:

System.Diagnostics.Debug.WriteLine(string.Format("Loading data started. Thread: {0}, {1}", System.Threading.Thread.CurrentThread.GetHashCode(), DateTime.Now));

IsBusy = true;
//Task.Factory.StartNew(() => GetDataBatch()); // UI is responsive
GetDataBatch(); // UI is not freezed (it is shown), but it is not responsive

System.Diagnostics.Debug.WriteLine(string.Format("Loading data completed. Thread: {0}, {1}", System.Threading.Thread.CurrentThread.GetHashCode(), DateTime.Now));

private async Task GetAll()
{
    if (_refreshA)
    {
        collA = new ObservableCollection(await Task.Run(() => GetA<A>()));
        _refreshA = false;

        System.Diagnostics.Debug.WriteLine(string.Format("GetA items loaded. Thread: {0}, {1}", System.Threading.Thread.CurrentThread.GetHashCode(), DateTime.Now));

    }

    if (_refreshB)
    {
        collB = new ObservableCollection(await Task.Run(() => GetB<B>(param)));
        _refreshB = false;

        System.Diagnostics.Debug.WriteLine(string.Format("GetB items loaded. Thread: {0}, {1}", System.Threading.Thread.CurrentThread.GetHashCode(), DateTime.Now));
    }

    System.Threading.Thread.Sleep(10000);

    if (_refreshC)
    {
        collC = new ObservableCollection(await Task.Run(() => GetC<C>()));
        _refreshC = false;

        System.Diagnostics.Debug.WriteLine(string.Format("GetC items loaded. Thread: {0}, {1}", System.Threading.Thread.CurrentThread.GetHashCode(), DateTime.Now));
    }
}

结果是:

Loading data started. Thread: 9, 15-Oct-12 02:35:00
Loading data completed. Thread: 9, 15-Oct-12 02:35:00
GetA items loaded. Thread: 9, 15-Oct-12 02:35:00
GetB items loaded.  Thread: 9, 15-Oct-12 02:35:01
GetC items loaded.  Thread: 9, 15-Oct-12 02:35:11

问题:UI 没有冻结(显示视图),但它也没有响应。例如,如果我将鼠标悬停在菜单项上,则什么也不会发生。我有一个在数据加载期间显示的模板(忙碌模板),它应该向用户指示正在发生的事情。此模板未显示,看起来它没有足够的 CPU 时间来绘制自己。如果我使用此代码:

Task.Factory.StartNew(() => GetDataBatch()); // UI is responsive
//GetDataBatch(); // UI is not freezed (it is shown), but it is not responsive

然后 UI 响应,繁忙的数据模板显示在屏幕上,但问题是现在所有收集数据都属于 UI 以外的其他线程,所以我无法从 UI 线程对它们进行任何操作。

async await 如何处理这个问题?

4

2 回答 2

3

使用 C# 5 async- await,这将非常简单:运行需要在后台线程上运行的代码,Task.Run()然后await使用Task, 异步等待它完成并在 UI 线程上恢复:

private async Task GetAll()
{
    if (_refreshA)
    {
        collA = new ObservableCollection(await Task.Run(() => GetA<A>()));
        _refreshA = false;
    }

    if (_refreshB)
    {
        collB = new ObservableCollection(await Task.Run(() => GetB<B>(param)));
        _refreshB = false;
    }

    if (_refreshC)
    {
        collC = new ObservableCollection(await Task.Run(() => GetC<C>()));
        _refreshC = false;
    }
}
于 2012-10-14T14:47:53.240 回答
1
void GetAll() {
    new Thread(() => {
        if (_refreshA) {
            var alist = GetA<A>();
            Dispatcher.Invoke(new Action(() => { collA = new ObservableCollection<A>(alist); }));
            _refreshA = false;
        }

        if (_refreshB) {
            var blist = GetB<B>(param);
            Dispatcher.Invoke(new Action(() => { collB = new ObservableCollection<B>(blist); }));
            _refreshB = false;
        }

        if (_refreshC) {
            var clist = GetC<C>();
            Dispatcher.Invoke(new Action(() => { collC = new ObservableCollection<C>(clist); }));
            _refreshC = false;
        }
    }).Start();
}

Dispatcher.Invoke保证每个动作在方法继续之前完成。只是为了比较,如果您想并行执行它们,则需要改为使用它们Dispatcher.BeginInvoke

于 2012-10-14T02:50:05.793 回答