myugaruの色々構想中・・・!

「C#」「画像処理」「XNA未対応PCでゲームIDE作りの無謀な野望」

ホーム 連絡をする 同期する ( RSS 2.0 ) Login
投稿数  98  : 記事  0  : コメント  2320  : トラックバック  59

ニュース

myugaru
仕事(昔)=ヲタク系プログラマー~マスコミ系サポートデスク
仕事(今)=電子機器系サービス業
趣味a=パズルゲーム全般、シューティングは主に見学
趣味b=画像処理関係の勉強
趣味c=プログラミング言語の勉強
趣味d=アキバ系ヲタク
趣味e=芸能アイドル系ヲタク
d,e色の強いもう一つのブログ
最新目標=シューティングゲームを作る

わんくまりんく

わんくま同盟blog C#,VB.NET掲示板

ぶろぐつーる

あわせて読みたい

はてなりんぐ

書庫

日記カテゴリ

ギャラリ

お友達

リンク

前エントリー:http://blogs.wankuma.com/myugaru/archive/2008/03/10/127092.aspx

siokoshouさんのコメントを拝見しまして私の説明力・説得力の無さを痛感しました。
ちょっと悔しいので今日は別の切り口で説明のリベンジをしたいと思います。

(また失敗に終わる予感もしますが・・・)

話はWindowsプログラミングです。

ボタン1をクリックすると画面の中央に
「ボタン1が押されました」
という文字が0.5秒間隔で点滅し、
ボタン2を押すとそれが消える

と仮定してください。

以下のようにプログラムを作ったとします。

ボタン1クリック()
{
 while (ボタン2フラグ == OFF) {
  while (0.5秒間)
   「ボタン1が押されました」表示
  while (0.5秒間)
   //何もしない
 }
}
 
ボタン2クリック()
{
 ボタン2フラグ = ON
}

一見仕様は満たされています。しかし上のプログラムは上手く動作しません。
ボタン1をクリックしてしまうと画面が真っ白になってしまうことでしょう。
誰でも一度くらいは経験があるフリーズという状態です。
タスクの切替が考慮されていないために起こる現象です。

改良したボタン1クリックが次のプログラムです。

ボタン1クリック()
{
 while (ボタン2フラグ == OFF) {
  while (0.5秒間) {
   「ボタン1が押されました」表示
   DoEvents();
  }
  while (0.5秒間) {
   DoEvents();
  }
 }
}

DoEventsはここではWindowsへ制御を渡す役割をする関数と思ってください。
・・・長く説明すると馬鹿にしてるとお叱りを受けますからここでやめます(苦笑

コルーチンで同じことを考えましょう。
ボタン1クリックは次のように書けます。

ボタン1クリック()
{
 while (ボタン2フラグ == OFF) {
  while (0.5秒間) {
   「ボタン1が押されました」表示
   yield return 適当;
  }
  while (0.5秒間) {
   yield return 適当;
  }
 }
}

コルーチンのyieldは状態をレジュームしメインループに制御を戻します。
次回の呼び出し時はyieldの次から再開されます。
DoEventsと全く同じですね。ここは大丈夫ですよね?

さてまたWindowsへ戻ります。
このボタン1クリックを呼び出しているのはWindowsならばWinProcに当ります。
そこにメインループが書かれています。

WinProc()
{
 while (true) {
  switch (イベント) {
   case ボタン1:
    ボタン1クリック();
    break;
   case ボタン2:
    ボタン2クリック();
    break;
  }
 }
}

上のコードには実は隠された処理が多く存在しています。
たとえばDoEventでの処理中断再開の機構が見えません。
しかしここで全て書くにはあまりに膨大です。
それにWindowsも一般的になってきたので省略します。

余談ですが。昔々Windows98がまだ健在だった時代。今も健在だとお叱り受けますね^^;;
自分の会社が初めて業務でWindowsを扱うことになった頃に上のような処理で、
「ボタン1の処理中にどうしてボタン2へ飛んでいけるのか」
を何日も何日も何日もかけて上司に説明した事を思い出しています(笑

話を戻します。私が実装を予定しているゲームメインはこうなります。
WinProcでは隠されていたと説明した部分もここでは露見します。

GameLoop()
{
 IEnumerator ボタン1タスク = ボタン1クリック();
 IEnumerator ボタン2タスク = ボタン2クリック();
 List タスクリスト
 タスクリスト.Add(ボタン1タスク)
 タスクリスト.Add(ボタン2タスク)
 while (true) {
   foreach (タスク in タスクリスト)(★1)
    if (!タスク.MoveNext()) {
     //終了したタスクを除外
     タスクリスト.Remove(タスク)
    }
  }
 }
}

★1:(15:40追記)実際には私の独自実装をほどこしたLinkedListを用います。

説明の本質から外れるので便宜上foreachを用いましたが現実のC#言語のforeachはノードを削除することはできません。

削除を考慮した独自のLinkedList拡張→http://blogs.wankuma.com/myugaru/archive/2008/03/06/126581.aspx

 

Windowsでの実装とコルーチンでの実装をご覧いただきました。
両者は

「処理を一旦中断し別タスクへ処理のチャンスを渡す」

という全く同じ基本思想で成り立っています。

●では再度お尋ねします。コルーチンのyieldは何を返すのでしょうか?

コルーチンのyieldが返す値についてもう一度だけ考えてみてほしいと思います。
列挙のyieldではありません。コルーチンのyieldです。

WindowsのDoEventsととらえてこれを再考するといかがでしょうか?
例題を見てもらうと両者の位置付けは全く同一です。
果たしてDoEventsは何を返すべきでしょう?ボタン1クリックの返値ではありませんよ。
yield returnがCurrentを返すと考えると上記でのDoEventsにとってのCurrentに該当するものは一体なんなのでしょうか?
ボタン1の所属する・・この場合はFormになるのでしょうか?
そうでなければ何であれば適当ですか?

またコルーチンを列挙とどうしてもとらえたいという方にはボタン1クリック()というハンドラーを列挙と捕らえることにどういう意味があるかを再考いただければと思います。

前エントリーから私の意見は「コルーチンのyieldの返値は無意味であり不要である」です。
DoEventsだって返値は現実にありませんものね。

お時間ありましたら再度皆さんのご感想などいただけたらうれしく思います。

ここまで読んでお付き合いくださった方に感謝いたします。ありがとうございます。

投稿日時 : 2008年3月11日 14:35

コメント

# re: コルーチンのyieldはDoEventsです 2008/03/11 15:07 siokoshou
あ~なるほど。GameLoop() のコードを見て納得できました。
yield で何か返してもそれを使う処理はないよってことなんですね。

それはそれとして、foreach の中でタスクリストの Remove をするのは無理です。詳しくは foreach のヘルプあたりを読んでみてください。forでまわすことになるかと思います。


# re: コルーチンのyieldはDoEventsです 2008/03/11 15:14 melt
まあ実際マイクロスレッドでは yield() とかそういう命令を書くだけでスレッドコンテキストが切り替わりますからね。返値は無くても構わないような気がします。
個人的にはマイクロスレッドというのは「手動でコンテキスト切り替えを制御できるマルチスレッド」という考え方がしっくり来ますね。
だから1つ1つの処理(スレッド)が独立している部分については実装がものすごく容易で、外部から状態変化が通知される部分については実装が(マルチスレッド程ではないけど)難しくなる。

# re: コルーチンのyieldはDoEventsです 2008/03/11 15:53 myugaru
コメントありがとうございます。

To siokoshouさん
>foreach の中でタスクリストの Remove をするのは無理です。
すみません。私がまたもや悪かったのです。説明をかなり端折っています。ForEachと書けばこういうご迷惑おかけせずに済みましたね。
修正いたしました。私ってほんと間抜けです。自己嫌悪です。

To meltさん
結局言語ってのが用意した実装をゆがめて使うと必ずこういった説明的な話をしないといけなくなります。
上のコード見てもIEnumerator タスクってなんじゃいって感じです。でもそれしか適当な実装がなかったので、これはもうどうしようもありません。
もはや言語思想とかの話になります。
色々難しいけどがんばります。時々見ててもらえると嬉しいです。


# re: コルーチンのyieldはDoEventsです 2008/03/11 18:22 ghost_shell
口を挟むことではないですけど、そこまで分析していてC#のイテレータ機能(yield return, yield break)を使わずに自力で仕組みを作ろうとは思わないの??

WF(Windows Workflow Foundation)チュートリアル 後編
http://codezine.jp/a/article/aid/1705.aspx

(Codezineの記事です。WFのステートマシンワークフローについて書かれています。)

参考にしろ、とは言いません。


薦めている体になってしまっていますが、読んでいません。

# re: コルーチンのyieldはDoEventsです 2008/03/11 22:19 myugaru
To ghost_shellさん
なるほどこういう技術があったんですね。
初めて知りました。目からうろこです。
自分の勉強不足を痛感しております。
私はC#Expressなのでこの技術には残念ながら触れることができませんが、大変参考になりました。
機会がありましたら利用できるようになりたいと思います。
本当にありがとうございました。


# http://oakleyfrogskinsholbrookradar.blinkweb.com/ 2013/03/17 3:07 Oakley Holbrook
What is your plan?All that glitters is not gold.This work itself is very easy.He is used to eating out all the timeHe came by train.This is the most wonderful day of my life, because I'm here with you now.This is the most wonderful day of my life, because I'm here with you now.The Smiths are my neighbors.The price is reasonable.I will speak against anything I know to be wrong.

Post Feedback

タイトル
名前
Url:
コメント