C#

BackgroundWorker を使用せずにスレッド上から画面情報へアクセスする方法

 

ボタンを押したときに時間が掛かる処理を実行させたい場合がある。
一般的には、BackgroundWorker を使うか、独自に作成したスレッドへ渡してUIがブロックされないように実装するが
ここでは、簡単なタスク実装を紹介する

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

    /// <summary>
    /// 簡易 子タスク
    /// </summary>
    class CoTask
    {
        private Control _dispatcher;

        public string Text { get; set; }

        public event EventHandler Update;



        public CoTask()
        {

            // Update イベントを処理するスレッドを固定する
            // このクラスをnewしたスレッドが親として使われる。
            this._dispatcher = new Control();
            this._dispatcher.CreateControl();

            this.Text = string.Empty;
        }


        public void Start()
        {
            // 別スレッドでRunを実行させる
            var th = new Thread(Run) { IsBackground = true };
            th.Start();

            // 又はスレッドプールからスレッドを借りる
            //ThreadPool.QueueUserWorkItem((x) => { Run(); });
        }


        protected virtual void Run()
        {
            int count = 0;

            while (true)
            {
                // ======================
                // 時間が掛かる処理
                // ======================
                Thread.Sleep(50);

                // ======================
                // 提供データ ごにょごにょ
                // ======================
                count++;
                this.Text = string.Format("Count: {0}, CoTask ThreadId:{1}", count, Thread.CurrentThread.ManagedThreadId);

                // ======================
                // イベントでUIへ通知
                // ======================
                OnUpdate();
            }
        }


        // イベント発火
        protected void OnUpdate()
        {
            var evt = this.Update; // イベントをスレッドセーフで扱うおまじない
            if (evt != null)
            {
                // OnUpdate を呼び出したているスレッドと CoTaskのインスタンスを作成したときのスレッドが違う場合
                // デリゲートを経由し CoTask 作成時のスレッドで実行させる
                if (this._dispatcher.InvokeRequired)
                {
                    //this._dispatcher.Invoke((Action)OnUpdate); // これで再起呼び出しも可能だが、直接デリゲート呼んだほうが無駄が無い
                    this._dispatcher.Invoke(evt, this, EventArgs.Empty);
                    return;
                }

                // 直接呼び出してよし!
                evt(this, EventArgs.Empty);
            }
        }
    }

以下に使用例を上げる。
継承してRun()で目的の実装をする

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


    public partial class MainForm : Form
    {

        CoTask _task;

        public MainForm()
        {
            InitializeComponent();

            // タスク作成
            _task = new CoTask();
            _task.Update += Task_Update;
            _task.Start();
        }

        private void Task_Update(object sender, EventArgs e)
        {
            var t = sender as CoTask;
            label1.Text = t.Text + string.Format(", Main ThreadId: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
        }



        #region Designer
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージ リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.label1.Location = new System.Drawing.Point(0, 0);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(33, 12);
            this.label1.TabIndex = 0;
            this.label1.Text = "TEST";
            // 
            // MainForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(583, 362);
            this.Controls.Add(this.label1);
            this.Name = "MainForm";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        #endregion

    }

 

タイトルとURLをコピーしました