C#里面的async/await语法简直好用的不要不要的。但是,如果我想让这种异步状态机,能工作在我自定义的线程里面要怎么办呢?

要让编译器识别async await 语法首先需要具备以下条件:
1、 可等待的对象,需要实现T GetAwaiter();方法。注意:T可以是任何类型,不一定是object类型。

可等待对象无返回值需要满足的条件: 1)、继承接口INotifyCompletion或实现void OnCompleted(Action continuation);方法。
2)、实现bool IsCompleted { get; }属性。 3)、实现void GetResult();方法。

可等待对象带返回值需要满足的条件: 1)、继承接口INotifyCompletion或实现void OnCompleted(Action continuation);方法。
2)、实现bool IsCompleted { get; }属性。 3)、实现T GetResult();方法。T可以是任意类型。

具备上述条件,你的代码就可以顺利编译通过了。但是此时的代码,还不能工作,因为你还没有给状态机增加状态!

为了增加这个状态,我们增加一个方法void ReportCompleted(T result);,用来更改状态机的状态值,表示我们要执行的异步操作是否已完成。

具体代码,如下所示:

相关的接口定义:


    /// <summary>
    /// 带返回值的可等待对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IAwaitable<T> : INotifyCompletion
    {
        bool IsCompleted { get; }
        T Result { get; }
        T GetResult();
        void ReportCompleted(T result);
    }

    /// <summary>
    /// 不带返回值的可等待对象
    /// </summary>
    public interface IAwaitable : INotifyCompletion
    {
        bool IsCompleted { get; }
        void GetResult();
        void ReportCompleted();
    }

    /// <summary>
    /// 获取可等待者
    /// </summary>
    public interface IAwaiter
    {
        IAwaitable GetAwaiter();
    }

    /// <summary>
    /// 获取可等待者
    /// </summary>
    public interface IAwaiter<T>
    {
        IAwaitable<T> GetAwaiter();
    }


    /// <summary>
    /// 不带返回值的可等待的任务
    /// </summary>
    public class AwaitableTask : IAwaiter
    {
        public AwaitableTask()
        {
            AwaitableObject = new AwaitableObject();
        }

        /// <summary>
        /// 获取可等待的对象
        /// </summary>
        public IAwaitable GetAwaiter()
        {
            return AwaitableObject;
        }

        /// <summary>
        /// 实现相关接口的可等待的对象
        /// </summary>
        public AwaitableObject AwaitableObject { get; private set; }
    }

    /// <summary>
    /// 带返回值的可等待的任务
    /// </summary>
    public class AwaitableTask<T> : IAwaiter<T>
    {
        public AwaitableTask()
        {
            AwaitableObject = new AwaitableObject<T>();
        }

        public IAwaitable<T> GetAwaiter()
        {
            return AwaitableObject;
        }

        public AwaitableObject<T> AwaitableObject { get; private set; }
    }

    /// <summary>
    /// 不带返回值的可等待的状态对象
    /// </summary>
    public class AwaitableObject : Awaitable, IAwaitable
    {
        public void GetResult()
        {

        }
    }

    /// <summary>
    /// 带返回值的可等待的状态对象
    /// </summary>
    public class AwaitableObject<T> : Awaitable, IAwaitable<T>
    {
        public T Result { get; private set; }

        public T GetResult()
        {
            return Result;
        }

        public void ReportCompleted(T result)
        {
            Result = result;
            ReportCompleted();
        }
    }

    /// <summary>
    /// 可等待的抽象类,状态机的具体实现
    /// </summary>
    public abstract class Awaitable
    {
        private Action _continuation;

        private readonly object _locker = new object();

        public bool IsCompleted { get; private set; }

        public void OnCompleted(Action continuation)
        {
            //因为此处调用和ReportCompleted,可能不在一个线程上,会出现时机问题
            lock (_locker)
            {
                if (IsCompleted)
                {
                    continuation?.Invoke();
                }
                else
                {
                    _continuation += continuation;
                }
            }
        }

        //用于通知,当前任务已经完成。
        public void ReportCompleted()
        {
            lock (_locker)
            {
                IsCompleted = true;
                var continuation = _continuation;
                _continuation = null;
                continuation?.Invoke();
            }
        }

        //重置状态,让状态机恢复可等待
        public void Reset()
        {
            lock (_locker)
            {
                _continuation?.Invoke();
                IsCompleted = false;
                _continuation = null;
            }
        }
    }

示例:


    public class TestElement : UIElement
    {
        //实例化一个可等待任务
        AwaitableTask _awaitableTask = new AwaitableTask();

        /// <summary>
        /// 指定的资源的路径
        /// </summary>
        public string SourceUri
        {
            get { return (string)GetValue(SourceUriProperty); }
            set { SetValue(SourceUriProperty, value); }
        }

        private ImageSource gifSource;

        public static readonly DependencyProperty SourceUriProperty = DependencyProperty.Register(nameof(SourceUri), typeof(string), typeof(TestElement), new PropertyMetadata(string.Empty, async (s, e) =>
        {
            var loc = s as TestElement;
            loc.gifSource = await LoadGifSourceAsync(e.NewValue as string);
            loc._awaitableTask.AwaitableObject?.ReportCompleted();
        }));

        public static async Task<ImageSource> LoadGifSourceAsync(string url)
        {
            await Task.Delay(500);
            return new BitmapImage(new Uri(url));
        }

        /// <summary>
        /// 等待源数据加载完成,对外开放的方法
        /// </summary>
        /// <returns></returns>
        public async Task WaitForLoaded()
        {
            await _awaitableTask;
        }

        public bool Do()
        {
            if (gifSource != null)
                return true;            
            return false;
        }
    }

    public class TestClass
    {
        public TestClass()
        {
            TestForNotWait();
            TestForWait();
        }


        public async void TestForWait()
        {
            TestElement testElement = new TestElement
            {
                SourceUri = @"D:\Users\huc\Pictures\Saved Pictures\1.jpeg"
            };
            await testElement.WaitForLoaded();
            Console.WriteLine("TestForWait Result:" + testElement.Do());
        }

        public void TestForNotWait()
        {
            TestElement testElement = new TestElement
            {
                SourceUri = @"D:\Users\huc\Pictures\Saved Pictures\1.jpeg"
            };

            Console.WriteLine("TestForNotWait Result:" + testElement.Do());            
        }
    }
        

欢迎转载分享,请关注微信公众号,将同步更新博客,方便查看!

承哥技术交流小作坊


本文会经常更新,请阅读原文: https://huchengv5.gitee.io//post/C-%E8%AE%A4%E8%AF%86asyncawait-%E5%A6%82%E4%BD%95%E8%87%AA%E5%B7%B1%E5%8A%A8%E6%89%8B%E7%BC%96%E5%86%99%E5%8F%AF%E7%AD%89%E5%BE%85%E7%9A%84%E4%BB%BB%E5%8A%A1.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名胡承(包含链接: https://huchengv5.gitee.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系