2013-02-18 47 views
0

我有遺留代碼,它在UI線程上執行一些非常長的操作。 我想要做的是顯示帶消息的進度條並在另一個線程上運行該工作。不幸的是,現在我沒有訪問VS2012,所以我不能使用async關鍵字。 我已經寫了一些代碼,它可以在0-1參數的操作下正常工作,並且使用動作沒有返回值。 但是當我嘗試調整它來支持Func時,我遇到了一些調用任務並返回TResult的問題 。 附加是我的原始代碼,將不勝感激任何建議。謝謝,奧馬爾在.NET 4中異步執行工作

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

您能給我們提供一些關於您所得到的問題的更多信息嗎? – 2013-02-18 09:45:35

+0

使用參數I創建任務的唯一API將傳遞一個對象作爲狀態。這是不合適的,因爲就像我說過的,我正在處理遺留代碼,這意味着我想將Func映射到現有方法,該方法可能直接使用並繼承類型作爲參數而不是Object。 – 2013-02-18 10:05:22

回答

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); 
    } 
} 

在這裏我添加了一個變化d版本根據您的需求

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(); 
    } 
} 
+0

感謝您的建議。然而,我正在尋找一種方法來異步完成這項工作,而對遺留代碼的影響最小。 – 2013-02-18 10:21:47

+0

你可以用它作爲參考 – IamStalker 2013-02-18 10:29:27

+0

如果你看看我的例子,我不需要改變現有代碼中的任何東西。我不直接調用方法,而是將其作爲Action傳遞給FreeProgressBar控件。如果我要使用你的代碼作爲參考,而不是每一種方法,我都必須寫一個xxxTask與匹配成員並調用它,而不是方法,對吧? – 2013-02-18 10:34:46