在.NET 4中异步执行工作

时间:2013-02-18 09:35:36

标签: c# multithreading asynchronous

我有遗留代码,可以在UI线程上执行一些非常长的操作。 我想要做的是显示带有消息的进度条并在另一个线程上运行该工作。不幸的是,目前我无法访问VS2012,因此我无法使用 async 关键字。 我已经编写了一些代码,它们可以正常运行0-1参数,并且使用 Action 没有返回值。 但是当我尝试调整它以支持Func时,我遇到了一些问题 调用任务并返回TResult。 附件是我的原始代码,将不胜感激任何建议。谢谢,Omer

    public partial class FreeProgressBarFrm : System.Windows.Forms.Form
    {

        #region Members

        /// <summary>
        /// timer for the progress bar.
        /// </summary>
        private Timer m_Timer = new Timer();

        /// <summary>
        /// Delegate for the background operation to perform.
        /// </summary>
        private Action m_backgroundOperation;

        /// <summary>
        /// Standard operation to show the user while the operation is in progress.
        /// </summary>
        private static readonly string m_performingUpdatesMessage = IO_Global.GetResourceString("Performing updates, please wait", "Performing updates, please wait", null);

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="backgroundDelegate"> Delegate for the background operation to perform</param>
        /// <param name="operationName">meessage to show the user while the operation is in progress.</param>
        public FreeProgressBarFrm(Action backgroundDelegate, string operationName)
        {
            InitializeComponent();
            m_backgroundOperation = backgroundDelegate;
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.lblOperation.Text = operationName;
            m_Timer.Interval = 1000;
            m_Timer.Tick += new EventHandler(m_Timer_Tick);

        }


        /// <summary>
        /// Constructor , for progressbar with defalt user message (performing updates, please wait).
        /// </summary>
        /// <param name="backgroundDelegate"> Delegate for the background operation to perform</param>
        /// <param name="operationName">operation display name</param>
        public FreeProgressBarFrm(Action backgroundDelegate): this(backgroundDelegate, m_performingUpdatesMessage)
        {
        }

        #endregion

        #region Methods

        /// <summary>
        /// Call this method to begin backgorund operation while
        /// showing the progress bar to the client.
        /// </summary>
        public void Wait()
        {
            ShowDialog(ControlsHelper.MainFrm);
        }

        /// <summary>
        /// Advance the progress bar
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_Timer_Tick(object sender, EventArgs e)
        {
            PerformStep();
        }

        /// <summary>
        /// Advance the progress bar
        /// </summary>
        private void PerformStep()
        {
            this.progressBar1.PerformStep();
            this.lblOperation.Refresh();

            if (this.progressBar1.Value == this.progressBar1.Maximum)
            {
                this.progressBar1.Value = this.progressBar1.Minimum;
            }
        }        

        /// <summary>
        /// Load the form , start the progress bar and backroud task.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProgressBarFrm_Load(object sender, EventArgs e)
        {
            m_Timer.Start();
            this.lblOperation.Refresh();

            Task task = new Task(m_backgroundOperation);
            Task UITask = task.ContinueWith(delegate { OnWorkCompleted(); },
            TaskScheduler.FromCurrentSynchronizationContext());
            try
            {
                task.Start();
            }
            catch (Exception)
            {
                Close();
                throw;
            }
        }

        /// <summary>
        /// Called when the work has been completed.
        /// </summary>
        private void OnWorkCompleted()
        {
            Close();
        }

        /// <summary>
        /// Close the timer.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProgressBarFrm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (m_Timer != null)
            {
                m_Timer.Dispose();
                m_Timer = null;
            }
        }


        #endregion
    }

1 个答案:

答案 0 :(得分:1)

这是我执行某些异步工作的方式,

public static class TaskExecuter
{
    private static readonly ThreadLocal<List<BackgroundTask>> TasksToExecute =
        new ThreadLocal<List<BackgroundTask>>(() => new List<BackgroundTask>());

    public static Action<Exception> ExceptionHandler { get; set; }

    public static void ExecuteLater(BackgroundTask task)
    {
        TasksToExecute.Value.Add(task);
    }

    public static void Discard()
    {
        TasksToExecute.Value.Clear();
    }

    public static void StartExecuting()
    {
        var value = TasksToExecute.Value;
        var copy = value.ToArray();
        value.Clear();

        if (copy.Length > 0)
        {
            Task.Factory.StartNew(() =>
            {
                foreach (var backgroundTask in copy)
                    ExecuteTask(backgroundTask);

            }, TaskCreationOptions.LongRunning)
            .ContinueWith(task =>
            {
                if (ExceptionHandler != null)
                    ExceptionHandler(task.Exception);

            }, TaskContinuationOptions.OnlyOnFaulted);
        }
    }

    public static void ExecuteTask(BackgroundTask task)
    {
        task.Run();
    }
}

这是基类

 public abstract class BackgroundTask
{
    protected readonly Logger Logger = LogManager.GetCurrentClassLogger();

    protected virtual void Initialize()
    {

    }

    protected virtual void OnError(Exception e)
    {
       //do some work
    }

    public bool? Run()
    {
        Logger.Info("Started task: {0}", GetType().Name);
        Initialize();
        try
        {
            Execute();
            TaskExecuter.StartExecuting();
            return true;
        }
        catch (Exception e)
        {
            Logger.ErrorException("Could not execute task " + GetType().Name, e);
            OnError(e);
            return false;
        }
        finally
        {
            TaskExecuter.Discard();
            Logger.Info("Finished task: {0}", GetType().Name);
        }
    }
    public abstract void Execute();
}

以下是使用

的示例
public class SendEmailTask : BackgroundTask
{
    private const string MailServerIp = "yourip";

    public string[] To { get; set; }
    public string From { get; set; }
    public string Template { get; set; }
    public object ViewContext { get; set; }
    public string[] Attachments { get; set; }
    public string Subject { get; set; }

    public override void Execute()
    {
        MailMessage message = new MailMessage();
        try
        {
            MailAddress mailAddress = new MailAddress(From);
            message.From = mailAddress;

            foreach (string to in To) message.To.Add(to);
            message.Subject = Subject;
            if (Attachments.ReturnSuccess())
            {
                foreach (string attachment in Attachments)
                    message.Attachments.Add(new Attachment(attachment));
            }
            message.Priority = MailPriority.High;
            message.Body = Template;
            message.AlternateViews
                   .Add(AlternateView
                   .CreateAlternateViewFromString(ViewContext.ToString(), new ContentType("text/html")));
            message.IsBodyHtml = true;
            new SmtpClient(MailServerIp)
            {
                Port = 25,
                UseDefaultCredentials = true
            }.Send(message);
        }
        catch (Exception e)
        {
            Logger.FatalException("Error sending email:", e);
        }
        finally
        {
            message.Dispose();
        }
    }

    public override string ToString()
    {
        return string.Format("To: {0}, From: {1}, Template: {2}, ViewContext: {3}, Attachments: {4}, Subject: {5}", To, From, Template, ViewContext, Attachments, Subject);
    }
}

在这里,我已根据您的需求添加了更改版本

public static class AsyncExecuter
{
    private static readonly ThreadLocal<List<Action>> TasksToExecute =
        new ThreadLocal<List<Action>>(() => new List<BackgroundTask>());

    public static Action<Exception> ExceptionHandler { get; set; }

    public static void ExecuteLater(BackgroundTask task)
    {
        TasksToExecute.Value.Add(task);
    }

    public static void Discard()
    {
        TasksToExecute.Value.Clear();
    }

    public static void StartExecuting()
    {
        var value = TasksToExecute.Value;
        var copy = value.ToArray();
        value.Clear();

        if (copy.Length > 0)
        {
            Task.Factory.StartNew(() =>
            {
                foreach (var backgroundTask in copy)
                    ExecuteTask(backgroundTask);

            }, TaskCreationOptions.LongRunning)
            .ContinueWith(task =>
            {
                if (ExceptionHandler != null)
                    ExceptionHandler(task.Exception);

            }, TaskContinuationOptions.OnlyOnFaulted);
        }
    }

    public static void ExecuteTask(Action task)
    {
        task.Invoke();
    }
}