代码改变世界

.NET APM之自制异步调用组件

2010-03-11 02:53  姜 萌@cnblogs  阅读(517)  评论(0编辑  收藏  举报

APM(=Asynchronous Programming Model(=异步编程模型))

.NET为我们提供了很多异步编程的方案,而在即将随.NET 4.0正式发布的F#更是为开发人员提供了强大的并行编程能力(http://www.infoq.com/cn/news/2009/11/pdc09-fsharphttp://msdn.microsoft.com/zh-cn/magazine/cc967279.aspx)。

CLR下的几种异步编程模型介绍

使用基于组件的方式进行异步编程:

ms为我们提供了一个现成的BackgroundWorker,它能够满足一般的异步编程需求,提供了很多事件(比如:ProgressChanged(在进度变更时),RunWorkerCompleted(当异步调用的方法完成之后)),配置好其属性和委托后调用DoWorkAsync即可开始异步过程。

使用经典的BeginXXX和EndXXX异步委托。

在.NET中,任何一个委托都能够使用BeginInvoke来使委托方法以异步的方式被调用。BeginInvoke会返回一个IAsyncResult对象,通过它能够获取异步过程的信息(比如是否已经完成,获得WaitHandle),可以使用AsyncCallback设定异步完成时的事件,也可以通过IAsyncResult.AsyncWaitHandle来等待异步过程,或是通过EndInvoke来等待过程结束。资料不少,这里不再复述。

园子里的参考:http://www.cnblogs.com/AndyHuang/archive/2008/12/24/1361267.html

我们也自制一个简易的提供异步过程调用组件。

下面罗列出其基本功能要求

能够方便的生成异步线程(下面称其为TaskHostThread),每个线程拥有一个可排序的任务队列,每个线程会按照任务的优先级去串行的执行他们。
TaskHostThread是可控的,即可以获取其运行状态并安全的被终止它。
TaskHostThread具有可复用性,在任务队列为空时进入等待状态,在新的任务加入后能立即激活。
能随时获得每个任务的状态(比如是否正在执行,是否处于暂停状态,是否已经被废弃)。
能够方便的、有选择性的将几个任务放在一个线程中去串行执行。
能够控制同一个线程中调用任务的优先级别,高级别的任务即使是之后加入的也能够提前得到运行的机会。

设计说明

IAsyncServiceDispatcher:用户能够使用IAsyncServiceDispatcher来注册任务到异步线程中,同时它还提供对线程 和 任务的细粒度控制(比如暂停,废弃,执行等)。

IHostThreadTable:这是一个存放每个ITaskHostThread实例的容器,IAsyncServiceDispatcher可以通过它来创建线程对象并对其进行检索等操作。

ITaskHostThread:负责线程创建、控制以及任务队列的处理。

ITaskHostThreadProvider:一个类型提供器,默认使用TaskHostThread这个实现类。

ITaskWrapper,ITask。ITask仅封装执行的代码,ITaskWrapper包装ITask,额外提供了诸如任务优先级、任务名称等属性。

TaskState:枚举,用于指示线程/任务所处的状态。

image image

image

如果需要将一个任务放到某一个线程中去异步执行可以使用类似如下的方法:void RegisterTask(string taskHostIdentity, ITaskWrapper task, ITaskPrincipal principal);taskHostIdentity表示任务分派到的那个线程的标识,ITaskWrapper 提供任务要执行的代码,ITaskPrincipal能够干预任务的执行行为。

其他:关于线程的挂起,使用类似while(……){Thread.Sleep(……)}这种轮回等待的方式是很耗费资源的,这里使用了

AutoResetEvent。

接口定义

public interface IAsyncServiceDispatcher
    {
        IHostThreadTable Table { get; }
        void RegisterTask(ITaskWrapper task);
        void RegisterTask(string taskHostIdentity, ITaskWrapper task);
        void RegisterTask(string taskHostIdentity, ITaskWrapper task, ITaskPrincipal principal);

        //对任务进行控制(只有实现ITaskWrapper才可以)
        void PauseTask(string hostThreadIdentity, string taskIdentity);
        void TerminateTask(string hostThreadIdentity, string taskIdentity);
        void ResumeTask(string hostThreadIdentity, string taskIdentity);

        //对线程进行控制
        void PauseThreadHost(string hostThreadIdentity);
        void TerminateThreadHost(string hostThreadIdentity);
        void ResumeThreadHost(string hostThreadIdentity);
        void TryTerminateAllThreadHost();
        /// <summary>
        /// 使用提供器来提供创建对象,是创建对象的类型不依赖于IHostThreadTable的实现。
        /// </summary>
        ITaskHostThreadProvider NewHostThreadProvider { get; set; }
    }


public interface IHostThreadTable
    {
        bool QueryExist(string name);
        ITaskHostThread this[string name] { get; set; }
        IEnumerable<string> Keys { get; }
    }


public interface ITask
    {
        TaskResult Call();
    }


public interface ITaskHostThread
    {
        TaskState CurrentState { get; }
        /// <summary>
        /// 用于指示线程状态的控制变量
        /// </summary>
        TaskState ExpectedState { get; set; }
        /// <summary>
        /// 开始执行,默认一个ITaskHostThread被创建后自动开始执行任务。
        /// </summary>
        void StartCurrent();
        /// <summary>
        /// 暂停当前任务并执行下一个(在手动恢复暂停任务之前不会自动回复状态)。
        /// </summary>
        void PauseCurrentAndNext();
        /// <summary>
        /// 暂停当前并不执行下一个,等待这个任务至手动恢复它。
        /// </summary>
        void PauseCurrent();
        /// <summary>
        /// 恢复当前暂停的(仅当为PauseAll(之后有效))
        /// </summary>
        void ResumeCurrent();
        /// <summary>
        /// 停止当前并进行下一个任务
        /// </summary>
        void TerminateCurrentAndNext();
        /// <summary>
        /// 停止当前任务
        /// </summary>
        void TerminateCurrent();
        /// <summary>
        /// 停止所有,这会导致任务队列被清空。
        /// </summary>
        void TerminateAll();
        /// <summary>
        /// 试图停止线程
        /// </summary>
        void TryExitThread();
        /// <summary>
        /// 新增加一个Task,如果没有实现IWrapperTask则自动命名其identify,优先级设为最低。
        /// 如果此任务有优先级,则不管此任务的优先级如何,都将在当前任务执行完后才能生效。
        /// </summary>
        /// <param name="task"></param>
        void AddTask(ITaskWrapper task);
        /// <summary>
        /// 新增加一个Task,如果没有实现IWrapperTask则自动命名其identify,优先级设为最低。
        /// 如果此任务有优先级并且优先级大于当前任务,则会暂停当前任务而执行新加入的任务。
        /// </summary>
        /// <param name="task"></param>
        void AddTaskAndRefresh(ITaskWrapper task);
        /// <summary>
        /// 根据identity移除任务(行为说明:如果任务没有identity(没有实现IWrapperTask)则不支持移除)
        /// </summary>
        /// <param name="identity"></param>
        void RemoveTask(string identity);
        /// <summary>
        /// 使队列中的某个任务处于暂停(等待执行)状态。
        /// </summary>
        /// <param name="identity"></param>
        void PauseTask(string identity);
        /// <summary>
        /// 使队列中的某个任务处于恢复状态。
        /// </summary>
        /// <param name="identity"></param>
        void ResumeTask(string identity);
        /// <summary>
        /// 获得当前正在运行的任务
        /// </summary>
        ITaskWrapper RunningTask { get; }
        /// <summary>
        /// 获得处于等待状态的任务
        /// </summary>
        IList<ITaskWrapper> PausingTask { get; }
    }


public interface ITaskHostThreadProvider
    {
        ITaskHostThread CreateTaskHostThread();
    }


/// <summary>
    /// 包装ITask以为其提供额外的特性,比如状态指示。
    /// </summary>
    public interface ITaskWrapper : ITask
    {
        ITask InnerTask { get; }
        TaskState State { get; set; }
        TaskState ExpectedState { get; set; }
        int Priority { get; set; }
        string Identify { get; set; }
    }

具体实现

namespace Sopaco.Lib.ServiceModel.AsyncServiceDispatcher
{
    public class AsyncServiceDispatcher : IAsyncServiceDispatcher
    {
        #region Fields
        private static readonly string DEFAULT_THREADHOSTIDENTITY = "Sopaco.Lib.ServiceModel.AsyncServiceDispatcher.DefaultThreadHostIdentity";
        private IHostThreadTable _table;
        #endregion

        #region Constructors & Initializer
        /// <summary>
        /// 为DI提供,不要添加代码
        /// </summary>
        protected AsyncServiceDispatcher()
        {
        }
        /// <summary>
        /// 如果使用此构造方法,则初始化一个默认的HostThreadTable。用此作为默认构造器。
        /// </summary>
        /// <param name="autoInit"></param>
        public AsyncServiceDispatcher(bool autoInit)
        {
            _table = new HostThreadTable(true);
            NewHostThreadProvider = new TaskHostThreadProvider();
        }
        public AsyncServiceDispatcher(IHostThreadTable table, ITaskHostThreadProvider provider)
        {
            _table = table;
            NewHostThreadProvider = provider;
        }
        #endregion

        #region IAsyncServiceDispatcher 成员

        public IHostThreadTable Table
        {
            get
            {
                return _table;
            }
            private set//设置一个私有set访问器,为使用DI提供便捷。
            {
                this._table = value;
            }
        }

        public void RegisterTask(ITaskWrapper task)
        {
            RegisterTask(DEFAULT_THREADHOSTIDENTITY, task);
        }

        public void RegisterTask(string taskHostIdentity, ITaskWrapper task)
        {
            RegisterTask(taskHostIdentity, task, null);
        }

        public void RegisterTask(string taskHostIdentity, ITaskWrapper task, ITaskPrincipal principal)
        {
            //判断taskHostIdentity指定的宿主是否存在,如果不存在先用Provider生成一个。
            if (!_table.QueryExist(taskHostIdentity))
            {
                var threadHost = NewHostThreadProvider.CreateTaskHostThread();
                _table[taskHostIdentity] = threadHost;
            }
            //将任务加入到队列中。
            _table[taskHostIdentity].AddTask(task);
        }

        public ITaskHostThreadProvider NewHostThreadProvider
        {
            get;
            set;
        }
        #endregion

        #region IAsyncServiceDispatcher 成员

        public void PauseTask(string hostThreadIdentity, string taskIdentity)
        {
            if(_table.QueryExist(hostThreadIdentity))
                return;
            _table[hostThreadIdentity].PauseTask(taskIdentity);
        }

        public void TerminateTask(string hostThreadIdentity, string taskIdentity)
        {
            if(_table.QueryExist(hostThreadIdentity))
                return;
            _table[hostThreadIdentity].RemoveTask(taskIdentity);
        }

        public void ResumeTask(string hostThreadIdentity, string taskIdentity)
        {
            if(_table.QueryExist(hostThreadIdentity))
                return;
            _table[hostThreadIdentity].ResumeTask(taskIdentity);
        }

        public void PauseThreadHost(string hostThreadIdentity)
        {
            if (!_table.QueryExist(hostThreadIdentity))
                return;
            _table[hostThreadIdentity].ExpectedState = TaskState.Pause;
        }

        public void TerminateThreadHost(string hostThreadIdentity)
        {
            if (!_table.QueryExist(hostThreadIdentity))
                return;
            _table[hostThreadIdentity].TryExitThread();
        }

        public void ResumeThreadHost(string hostThreadIdentity)
        {
            if (!_table.QueryExist(hostThreadIdentity))
                return;
            _table[hostThreadIdentity].ResumeCurrent();
        }

        public void TryTerminateAllThreadHost()
        {
            var keys = _table.Keys;
            foreach(string key in keys)
            {
                _table[key].TryExitThread();
            }
        }
        #endregion
    }
}

namespace Sopaco.Lib.ServiceModel.AsyncServiceDispatcher
{
    public class HostThreadTable : IHostThreadTable
    {
        #region Fields
        private IDictionary<string, ITaskHostThread> _table;
        #endregion

        #region Constructors & Initializer
        protected HostThreadTable()//为DI提供,不要放入任何代码。
        {

        }
        /// <summary>
        /// Sopaco设计契约,为了使IoC正确的注入属性,使用此构造器作为普通的手动初始化构造器。
        /// </summary>
        /// <param name="autoCreateContainer"></param>
        public HostThreadTable(bool autoCreateContainer)
        {
            _table = new Dictionary<string, ITaskHostThread>();
        }
        #endregion

        #region IHostThreadTable 成员

        public bool QueryExist(string name)
        {
            return _table.ContainsKey(name);
        }

        public IEnumerable<string> Keys
        {
            get
            {
                return _table.Keys;
            }
        }

        public ITaskHostThread this[string name]
        {
            get
            {
                if(!queryExist(name))
                {
                    return null;
                }
                else
                {
                    return _table[name];
                }
            }
            set
            {
                if (queryExist(name))
                {
                    throw new InvalidOperationException("已经存在相同命名的TaskHostThread:" + name);
                }
                _table[name] = value;
            }
        }
        #endregion

        #region private Helper Methods
        private bool queryExist(string name)//QueryExist方法是对外提供的契约,queryExist是对内提供的辅助,尽管目前实现相同,但应该分开为好。
        {
            return QueryExist(name);
        }
        #endregion
    }
}

namespace Sopaco.Lib.ServiceModel.AsyncServiceDispatcher
{
    public class TaskHostThread : ITaskHostThread
    {
        #region Fields
        private Thread _thread;
        private List<ITaskWrapper> _task;
        private TaskState _currentState = TaskState.Ready;
        private AutoResetEvent _waitTaskEvent;
        private AutoResetEvent _waitResumeEvent;
        private object _syncRoot = new object();
        #endregion

        #region Constructors & Initializer
        protected TaskHostThread()
        {

        }
        public TaskHostThread(bool autoInit) : this(TaskState.Running)
        {
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="state">可以通过此参数控制是否立即执行任务</param>
        public TaskHostThread(TaskState state)
        {
            ExpectedState = state;
            Init();
            if (state == TaskState.Running)
                StartCurrent();
        }
        private void Init()
        {
            _waitTaskEvent = new AutoResetEvent(true);
            _waitResumeEvent = new AutoResetEvent(true);
            _thread = new Thread(this.runBody);
            _task = new List<ITaskWrapper>();
        }
        #endregion
        #region ITaskHostThread 成员
        public TaskState CurrentState
        {
            get { return _currentState; }
        }

        public TaskState ExpectedState
        {
            get;
            set;
        }

        public void TryExitThread()
        {
            ExpectedState = TaskState.Abort;
            //释放所有事件
            _waitResumeEvent.Set();
            _waitTaskEvent.Set();
        }

        public void StartCurrent()
        {
            if (_thread.ThreadState == ThreadState.Unstarted)
                _thread.Start();
        }

        public void PauseCurrentAndNext()
        {
            throw new NotImplementedException("此版本无法对任务进行细力度的控制");
        }

        public void PauseCurrent()
        {
            ExpectedState = TaskState.Pause;
        }

        public void ResumeCurrent()
        {
            _waitResumeEvent.Set();
            ExpectedState = TaskState.Running;
        }

        public void TerminateCurrentAndNext()
        {
            throw new NotImplementedException("此版本无法终止正在任务");
        }

        public void TerminateCurrent()
        {
            throw new NotImplementedException("此版本无法终止正在任务");
        }

        public void TerminateAll()
        {
            foreach(var tk in _task)
            {
                tk.ExpectedState = TaskState.Abort;
            }
        }

        public void AddTask(ITaskWrapper task)
        {
            lock (_syncRoot)
            {
                if (_task.Exists(p => p.Identify.Equals(task.Identify)))
                    throw new InvalidOperationException("任务已经存在:" + task.Identify);
                _task.Add(task);
                if(_task.Count == 1)
                    _waitTaskEvent.Set();
            }
        }

        public void AddTaskAndRefresh(ITaskWrapper task)
        {
            lock(_syncRoot)
            {
                _task.Add(task);
                _waitTaskEvent.Set();
                resortTask();
            }
        }

        public void RemoveTask(string identity)
        {
            ITaskWrapper task = _task.Where(p => p.Identify.Equals(identity)).FirstOrDefault();
            if(task != null)
            {
                _task.Remove(task);
            }
        }

        public void PauseTask(string identity)
        {
            var task = _task.Where(p => p.Identify == identity).FirstOrDefault();
            if (task == null)
                return;
            task.ExpectedState = TaskState.Pause;
        }
        public void ResumeTask(string identity)
        {
            var task = _task.Where(p => p.Identify == identity).FirstOrDefault();
            if (task == null)
                return;
            task.ExpectedState = TaskState.Running;
        }
        /// <summary>
        /// 返回当前运行的任务,如果没有则返回null
        /// </summary>
        public ITaskWrapper RunningTask
        {
            get
            {
                return _task.Where(p => p.State == TaskState.Running).First();
            }
        }

        public IList<ITaskWrapper> PausingTask
        {
            get
            {
                return _task.Where( p => p.State == TaskState.Pause).ToList();
            }
        }

        #endregion

        #region private Helper Methods
        private void runBody()
        {
            _currentState = TaskState.Ready;
            while(ExpectedState != TaskState.Complete)
            {
                //响应暂停命令
                if(ExpectedState == TaskState.Abort)
                {
                    waitUntilResume();
                    _currentState = TaskState.Running;
                }
                if(ExpectedState == TaskState.Abort)
                    break;
                //如果没有任务进入等待状态,当队列中加入任务时激活信号量
                if(this._task.Count == 0)
                {
                    waitUntilHasTask();
                    _currentState = TaskState.Running;
                }
                if (ExpectedState == TaskState.Abort)
                    break;
                //如果存在任务则取出并执行。
                ITaskWrapper task = null;
                lock(_syncRoot)
                {
                    if (_task.Count != 0)
                    {
                        task = dequeueTask();
                        if (task.State == TaskState.Pause)
                        {
                            enqueueTask(task);
                            task = null;
                        }
                    }
                }
                if (ExpectedState == TaskState.Abort)
                    break;
                if (task != null)
                    task.Call();//执行
            }
            _currentState = TaskState.Complete;
        }
        private void waitUntilHasTask()
        {
            _currentState = TaskState.Free;//线程进入Free状态
            _waitTaskEvent.Reset();
            _waitTaskEvent.WaitOne();
        }
        private void waitUntilResume()
        {
            _currentState = TaskState.Pause;
            _waitResumeEvent.Reset();
            _waitResumeEvent.WaitOne();
        }
        private ITaskWrapper dequeueTask()
        {
            var task = _task.First();
            _task.Remove(task);
            return task;
        }
        private void enqueueTask(ITaskWrapper task)
        {
            _task.Add(task);
        }
        private void resortTask()
        {
            _task.Sort((p, q) =>
                {
                    if (p.State == TaskState.Pause)
                        return -1;
                    else if (q.State == TaskState.Pause)
                        return 1;
                    else
                        return q.Priority - p.Priority;
                });
        }
        #endregion
    }
}

public class TaskHostThreadProvider : ITaskHostThreadProvider
    {
        #region ITaskHostThreadProvider 成员

        public ITaskHostThread CreateTaskHostThread()
        {
            return new TaskHostThread(true);
        }

        #endregion
    }

public enum TaskResult
    {
        OK
    }

public enum TaskState
    {
        Ready,
        Running,
        Pause,
        Abort,
        Complete,
        Free
    }

public class TaskWrapperBase : ITaskWrapper
    {
        #region Fields
        private static readonly string DEFAULT_TASKIDENTITY = "Sopaco.Lib.ServiceModel.AsyncServiceDispatcher.TaskWrapperBaseIdentity";
        private ITask _innerTask;
        #endregion

        #region Constructors & Initializer
        private TaskWrapperBase()
        {

        }
        public TaskWrapperBase(bool autoInit, ITask innerTask)
            : this(innerTask, DEFAULT_TASKIDENTITY, 0, TaskState.Ready)
        {
        }
        public TaskWrapperBase(ITask innerTask, string identity, int priority, TaskState state)
        {
            _innerTask = innerTask;
            this.Identify = identity;
            this.Priority = priority;
            this.State = state;
        }
        #endregion

        #region ITaskWrapper 成员

        public ITask InnerTask
        {
            get
            {
                return _innerTask;
            }
        }

        public TaskState State
        {
            get;
            set;
        }

        public TaskState ExpectedState
        {
            get;
            set;
        }

        public int Priority
        {
            get;
            set;
        }

        public string Identify
        {
            get;
            set;
        }

        #endregion

        #region ITask 成员

        public TaskResult Call()
        {
            _innerTask.Call();
            return TaskResult.OK;
        }

        #endregion
    }


project放到sky driver上了:http://public.blu.livefilestore.com/y1ppmpl1LORAqLOBcxdTkZhcnwLaSsBb_zxte5DdifEL8v85lIWMnPJpmS6sK5Ait7gEV4zb9u77JACyVfMqNu9Pg/AsyncServiceDispatcher.rar?download