読者です 読者をやめる 読者になる 読者になる
ようこそ。睡眠不足なプログラマのチラ裏です。

yield returnでマイクロスレッド。んで、かえるうたの輪唱。

プログラミング C#3.0

コルーチンでマイクロスレッドなクラスを作成します。

using System;
using System.Collections.Generic;
using System.Collections;
using System.Threading;

namespace WindowsApplication1
{
    /// <summary>
    /// MicroThread
    /// </summary>
    public class MicroThread
    {
        private List<CoRoutine> threads = new List<CoRoutine>();

        public IEnumerable NextThread()
        {
            while (true)
            {
                var allDone = true;
                foreach (CoRoutine thread in threads)
                {
                    if (thread.IsDone)
                    {
                        allDone = false;
                        yield return thread.Process();
                    }
                }
                if (allDone) yield break;
            }
        }

        public void Add(IEnumerable en)
        {
            threads.Add(new CoRoutine(en));
        }

        public void Execute(){
            new Thread(new ThreadStart(CreateThread)).Start();
        }

        private void CreateThread(){
            foreach (var e in this.NextThread()) ;
        }

        /// <summary>
        /// CoRoutine
        /// </summary>
        private class CoRoutine
        {
            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="en"></param>
            public CoRoutine(IEnumerable en)
            {
                this.en = en.GetEnumerator();
            }

            private IEnumerator en { get; set; }
            public bool IsDone {
                get{ return en.MoveNext(); }
            }

            private object Current
            {
                get { return en.Current; }
            }

            public object Process()
            {
                if (IsDone) return en.Current;
                return null;
            }
        }
    }
}


んで、試しに使ってみます。
「かえるのうた」の輪唱で並列処理っぽい雰囲気を出してみる。

using System;
using System.Collections.Generic;
using System.Collections;
using System.Windows.Forms;

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            //Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void btnKaeru_Click(object sender, EventArgs e)
        {
            this.textBox1.Clear();
            this.textBox2.Clear();
            this.textBox3.Clear();

            var mt = new MicroThread();
            mt.Add(kaeru1(x => textBox1.AppendText(x)));
            mt.Add(kaeru2(x => textBox2.AppendText(x)));
            mt.Add(kaeru3(x => textBox3.AppendText(x)));
            mt.Execute();
        }

        private object AppendText(Action<string> proc,string s)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(proc, new object[]{s});
                return null;
            }
            proc.Invoke(s);
            return null;
        }

        private IEnumerable kaeru1(Action<string> f)
        {
            yield return AppendText(f, "かえるのうたが\n");
            yield return AppendText(f, "きこえてくるよ\n");
            yield return AppendText(f, "グワッグワッグワッグワッ\n");
            yield return AppendText(f, "ゲロゲロゲロゲロ\n");
            yield return AppendText(f, "グワッグワッグワッ\n");
            yield break;
        }

        private IEnumerable kaeru2(Action<string> f)
        {
            yield return AppendText(f, "\n");
            yield return AppendText(f, "かえるのうたが\n");
            yield return AppendText(f, "きこえてくるよ\n");
            yield return AppendText(f, "グワッグワッグワッグワッ\n");
            yield return AppendText(f, "ゲロゲロゲロゲロ\n");
            yield return AppendText(f, "グワッグワッグワッ\n");
            yield break;
        }

        private IEnumerable kaeru3(Action<string> f)
        {
            yield return AppendText(f, "\n");
            yield return AppendText(f, "\n");
            yield return AppendText(f, "かえるのうたが\n");
            yield return AppendText(f, "きこえてくるよ\n");
            yield return AppendText(f, "グワッグワッグワッグワッ\n");
            yield return AppendText(f, "ゲロゲロゲロゲロ\n");
            yield return AppendText(f, "グワッグワッグワッ\n");
            yield break;
        }
    }
}

おー、いい具合にコルーチンがマイクロスレッドっぽく振舞ってますね。
それはそうと、yield return null;ってやるのはどうも気持ち悪いです。
C#4.0では、yield return null;って書かなくてもよいようにしてもらえると嬉しい。