将使用backgroundworker的方法替换为async / tpl(.NET 4.0) [英] Replacing methods that use backgroundworker to async / tpl (.NET 4.0)

查看:171
本文介绍了将使用backgroundworker的方法替换为async / tpl(.NET 4.0)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题很多。自从我看到NET 4.5,我印象非常深刻。不幸的是,我所有的项目都是.NET 4.0,我并没有考虑迁移。所以我想简化我的代码。



目前,我的大部分代码通常需要足够的时间冻结屏幕,我执行以下操作:

  BackgroundWorker bd = new BackgroundWorker(); 
bd.DoWork + =(a,r)=>
{
r.Result = ProcessMethod(r.Argument);
};
bd.RunWorkerCompleted + =(a,r)=>
{
UpdateView(r.Result);
};

bd.RunWorkerAsync(args);

老实说,我很厌倦了。当有一个逻辑复杂的用户交互时,这成为一个大问题。



我想知道,如何简化这个逻辑? (记住我在与Net 4.0)我注意到了一些谷歌,但没有发现任何容易实现和适合我的需要。



我以为这个解决方案如下:

  var foo = args as富; 
var result = AsyncHelper.CustomInvoke&Foo>(ProcessMethod,foo);
UpdateView(result);

public static class AsyncHelper
{
public static T CustomInvoke< T>(Func< T,T> func,T param)其中T:class
{
T result = null;
DispatcherFrame frame = new DispatcherFrame();
Task.Factory.StartNew(()=>
{
result = func(param);
frame.Continue = false;
});

Dispatcher.PushFrame(frame);

返回结果;
}
}

我不知道影响是操纵调度器框架。
但是我知道它会很好的工作,例如,我可以在控件的所有事件中使用它,而不用冻结屏幕。
我对泛型类型,协方差,逆向的知识是有限的,也许这个代码可以改进。



我想到了使用 Task.Factory.StartNew Dispatcher.Invoke ,但没有什么似乎有趣和简单的使用。任何人都可以给我一些光?

解决方案

你应该使用任务并行库(TPL)。关键是为更新UI的任何延续指定当前 SynchronizationContext TaskScheduler 。例如:

  Task.Factory.StartNew(()=> 
{
return ProcessMethod你的参数);
})
.ContinueWith(antecedent =>
{
UpdateView(antecedent.Result);
},
TaskScheduler.FromCurrentSynchronizationContext ));

除了访问先前的结果属性,那就是这一切。通过使用 FromCurrentSynchronizationContext()来自WPF的环境SynchronizationContext(即DispatcherSynchronizationContext)将用于执行继续。这与调用 Dispatcher相同。[开始]调用,但是您完全从中抽象出来。



如果你想要获得更清洁,如果你控制ProcessMethod我实际上会重写,以返回一个任务,让它拥有如何得到旋转(仍然可以使用 StartNew 内部)。这样你可以从ProcessMethod可能想要自己做的异步执行决策中抽象出调用者,而只需要担心连续继续等待结果。



更新5/22/2013



应该注意的是随着.NET 4.5的出现和C#中的异步语言支持这个规定的技术是过时的,你可以简单地依靠这些功能来执行一个特定的任务,使用等待Task.Run ,然后执行之后将再次发生在Dispatcher线程上自动。所以这样的东西:

  MyResultType processingResult = await Task.Run(()=> 
{
return ProcessMethod(yourArgument);
});

UpdateView(processingResult);


My questions are many. Since I saw. NET 4.5, I was very impressed. Unfortunately all my projects are .NET 4.0 and I am not thinking about migrating. So I would like to simplify my code.

Currently, most of my code that usually take enough time to freeze the screen, I do the following:

BackgroundWorker bd = new BackgroundWorker();
bd.DoWork += (a, r) =>
    {
        r.Result = ProcessMethod(r.Argument);
    };
bd.RunWorkerCompleted += (a, r)  =>
    {
        UpdateView(r.Result);
    };

bd.RunWorkerAsync(args);

Honestly, I'm tired of it. And that becomes a big problem when there is a logic complex user interaction.

I wonder, how to simplify this logic? (Remember that I'm with. Net 4.0) I noticed a few things by google, but not found anything easy to implement and suitable for my needs.

I thought this solution below:

var foo = args as Foo;
var result = AsyncHelper.CustomInvoke<Foo>(ProcessMethod, foo);
UpdateView(result);

public static class AsyncHelper
{
    public static T CustomInvoke<T>(Func<T, T> func, T param) where T : class
    {
        T result = null;
        DispatcherFrame frame = new DispatcherFrame();
        Task.Factory.StartNew(() =>
        {
            result = func(param);
            frame.Continue = false;
        });

        Dispatcher.PushFrame(frame);

        return result;
    }
}

I am not sure about the impact is on manipulating the dispatcher frame. But I know That it would work very well, for example, I could use it in all the events of controls without bothering to freeze the screen. My knowledge about generic types, covariance, contravariance is limited, maybe this code can be improved.

I thought of other things using Task.Factory.StartNew and Dispatcher.Invoke, but nothing that seems interesting and simple to use. Can anyone give me some light?

解决方案

You should just use the Task Parallel Library (TPL). The key is specifying the TaskScheduler for the current SynchronizationContext for any continuations in which you update the UI. For example:

Task.Factory.StartNew(() =>
{
    return ProcessMethod(yourArgument);
})
.ContinueWith(antecedent =>
{
    UpdateView(antecedent.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());

Aside from some exception handling when accessing the antecedent's Result property, that's all there is too it. By using FromCurrentSynchronizationContext() the ambient SynchronizationContext that comes from WPF (i.e. the DispatcherSynchronizationContext) will be used to execute the continuation. This is the same as calling Dispatcher.[Begin]Invoke, but you are completely abstracted from it.

If you wanted to get even "cleaner", if you control ProcessMethod I would actually rewrite that to return a Task and let it own how that gets spun up (can still use StartNew internally). That way you abstract the caller from the async execution decisions that ProcessMethod might want to make on its own and instead they only have to worry about chaining on a continuation to wait for the result.

UPDATE 5/22/2013

It should be noted that with the advent of .NET 4.5 and the async language support in C# this prescribed technique is outdated and you can simply rely on those features to execute a specific task using await Task.Run and then execution after that will take place on the Dispatcher thread again automagically. So something like this:

MyResultType processingResult = await Task.Run(() =>
{
    return ProcessMethod(yourArgument);
});

UpdateView(processingResult);

这篇关于将使用backgroundworker的方法替换为async / tpl(.NET 4.0)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆