0

UIスレッドで非常に長い操作を実行するレガシーコードがあります。私がやりたいのは、メッセージ付きのプログレスバーを表示し、別のスレッドで作業を実行することです。残念ながら、今のところVS2012にアクセスできないため、asyncキーワードを使用できません。Actionを使用して、0-1パラメーターの操作で正常に動作し、戻り値がないコードをいくつか作成しました。しかし、Funcをサポートするように調整しようとすると、タスクの呼び出しとTResultの返送でいくつかの問題が発生しました。添付されているのは私の元のコードです。何か提案をいただければ幸いです。ありがとう、オメル

    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
    }
4

1 に答える 1

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();
    }
}
于 2013-02-18T09:46:22.337 に答える