迁移到.net core要点: 修改Delegate.BeginInvoke

异步委托是c#项目从.net framework移植到.net core一个很让人头疼的问题。虽然.net framework会有其他一些方法无法兼容,但异步委托令人头疼的就是它不会在.net standard里报错,即使它并不能在.net core里生效。最近在处理项目兼容性的时候经常会遇到这个问题,在此做一下记录。

关于BeginInvoke

我们先来了解一下这个API的工作内容。 委托中的BeginInvoke相当于新开了一个线程去运行方法,让这个方法在后台运行。而EndInvoke就是返回该方法的返回值。

static void Main(string[] args)
        {
            Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主线程ID
            SayHello sayhello = new SayHello(Say);
            IAsyncResult iResult=sayhello.BeginInvoke("Olive", null, null);//Delegate.BeginInvoke()方法实际上是一个IAsyncResult的对象
            string result = sayhello.EndInvoke(iResult);//Delegate.EndInvoke()方法就是返回调用函数的返回值,但是该方法需要一个IAsyncResult对象,也就是委托调用BeginInvoke()开始异步
            Console.WriteLine(result);
            Console.ReadKey();
        }
        private static string  Say(string name)
        {
            Console.WriteLine("Hello!--------" + name);
            Console.WriteLine("当前的线程ID为:" + Thread.CurrentThread.ManagedThreadId);
            return "I Love you " + name;
        }

一般为了防止EndInvoke阻塞,会利用等待句柄。

if (iResult.AsyncWaitHandle.WaitOne(1000,false)) //如果等待超过1秒,则无法返回结果
        {
            string result = sayhello.EndInvoke(iResult);
            Console.WriteLine(result);
        }

为什么可移植性分析器没有报告

因为这些委托的方法并不是在.net framework框架的库里声明的。 .NET编译时会将高级语言编译成一种称作IL的中间语言,编译时IL编译库中会给委托添加BeginInvoke和EndInvoke方法,而可移植性分析器只会报告在.net framework框架的库里声明的方法。

如何修改

我们可以用Task来代替实现异步。 举个例子

delegate int WorkDelegate(int arg);
...
WorkDelegate del = DoWork;

// Calling del.BeginInvoke starts executing the delegate on a
// separate ThreadPool thread
Console.WriteLine("Starting with BeginInvoke");
var result = del.BeginInvoke(11, WorkCallback, null);

// This writes output to the console while DoWork is running in the background
Console.WriteLine("Waiting on work...");

// del.EndInvoke waits for the delegate to finish executing and 
// gets its return value
var ret = del.EndInvoke(result);

可以用Task.Run来代替BeginInvoke,await来代替EndInvoke,达到异步运行的效果。

delegate int WorkDelegate(int arg);
...
WorkDelegate del = DoWork;

// Schedule the work using a Task and 
// del.Invoke instead of del.BeginInvoke.
Console.WriteLine("Starting with Task.Run");
var workTask = Task.Run(() => del.Invoke(11));

// Optionally, we can specify a continuation delegate 
// to execute when DoWork has finished.
var followUpTask = workTask.ContinueWith(TaskCallback);

// This writes output to the console while DoWork is running in the background.
Console.WriteLine("Waiting on work...");

// We await the task instead of calling EndInvoke.
// Either workTask or followUpTask can be awaited depending on which
// needs to be finished before proceeding. Both should eventually
// be awaited so that exceptions that may have been thrown can be handled.
var ret = await workTask;
await followUpTask;

本文章使用limfx的vscode插件快速发布