Hatena::ブログ(Diary)

匣の向こう側 - あまりに.NETな RSSフィード

2006-01-23(Mon)

[][]Delegate.BeginInvokeに重い処理はさせるな

Delegateを使って手軽に非同期処理を行うことが出来ますが、これには落とし穴があります。

using System;
using System.Threading;

delegate void Rush();

class Program
{
    static void TheWorld()
    {
        Console.WriteLine("\nザ・ワールド!! 時よ止まれ!\n");
        while (true)
            Thread.Sleep(Int16.MaxValue);
    }

    static void Muda()
    {
        Console.Write("無駄");
        while (true)
            Thread.Sleep(Int16.MaxValue);
    }

    static void Mudaa()
    {
        Console.WriteLine("無駄ァ!!!!");
        Thread.Sleep(1000);
        Console.WriteLine("\nそして時は動き出す\n");
        Thread.Sleep(1000);
    }

    static void Time(object o, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine(e.SignalTime);
    }

    static void Main(string[] args)
    {
        System.Timers.Timer t = new System.Timers.Timer();
        t.Elapsed += new System.Timers.ElapsedEventHandler(Time);
        t.Interval = 1000;
        t.Start();

        Thread.Sleep(3000);

        new Rush(TheWorld).BeginInvoke(null, null);
        for (int i = 0; i < 23; ++i)
            new Rush(Muda).BeginInvoke(null, null);
            // こっちだと時は止まりません
            // new Thread(new ThreadStart(Muda)).Start(); 
        IAsyncResult result = new Rush(Mudaa).BeginInvoke(null, null);
        result.AsyncWaitHandle.WaitOne();
        Thread.Sleep(1000);
    }
}

/*
2006/01/23 22:43:16
2006/01/23 22:43:17

ザ・ワールド!! 時よ止まれ!

無駄無駄(以下略)無駄ァ!!!!

そして時は動き出す

2006/01/23 22:43:31
(以下略)
2006/01/23 22:43:32
 */

上記を見ての通り、大量にBeginInvokeを行うとTimers.Elapsedが影響を受けます。実はBeginInvokeもElapsedもスレッドプールを使用していて、デフォルトで1CPU、1プロセスあたり25スレッドという制限があるためです。なので、BeginInvokeには時間の掛かる処理は行わせるべきではありません。ちなみにコメントアウトしているThreadを使った処理の場合は、スレッドプールを使わないので時は止まりません。(^^;

通りすがり通りすがり 2008/01/28 12:34 やっぱり25スレッド制限なんてあったんですね・・・
非同期なのに処理が重くなるのに疑問を感じていましたので助かりました。

akirameiakiramei 2008/01/28 21:11 お役に立ててなによりです。(^^)

yyamasakyyamasak 2008/07/08 13:30 上のコードそのままコンパイルしてみたら時が止まりませんでしたが、
デュアルコアだからですかねえ?

2008/07/08 13:17:55
2008/07/08 13:17:56
2008/07/08 13:17:57

ザ・ワールド!! 時よ止まれ

無駄無駄2008/07/08 13:17:58
無駄無駄2008/07/08 13:17:59
無駄無駄2008/07/08 13:18:00
無駄無駄2008/07/08 13:18:01
無駄2008/07/08 13:18:02
無駄2008/07/08 13:18:03
無駄2008/07/08 13:18:04
無駄2008/07/08 13:18:05
無駄2008/07/08 13:18:06
無駄2008/07/08 13:18:07
無駄2008/07/08 13:18:08
無駄2008/07/08 13:18:09
無駄2008/07/08 13:18:10
無駄2008/07/08 13:18:11
無駄2008/07/08 13:18:12
無駄2008/07/08 13:18:13
無駄2008/07/08 13:18:14
無駄2008/07/08 13:18:15
無駄2008/07/08 13:18:16
無駄ァ!!!!

そして時は動き出す

2008/07/08 13:18:17
2008/07/08 13:18:18
2008/07/08 13:18:19

akirameiakiramei 2008/07/08 13:45 http://msdn.microsoft.com/ja-jp/library/h4732ks0.aspx
↑によると、1CPU(Core)あたり25スレッドなのでデュアルコアだと倍は耐えられると思われます。

yyamasakyyamasak 2008/07/08 14:51 for (int i = 0; i < 100; ++i)
new Rush(Muda).BeginInvoke(null, null);

とかやっても止まらなかったので、調べてみたらこんなページを見つけました。
最新の環境でやると250スレッドに増えてるようです。
http://www.infoq.com/jp/news/2008/05/ThreadPool

これだけ増えるとデッドロックはほとんど起こらなくなるらしく
for (int i = 0; i < 1000; ++i)
new Rush(Muda).BeginInvoke(null, null);

とかやっても止まらないです。

ザワールドが見れない!て違うか。喜ばしい改善。

akirameiakiramei 2008/07/09 01:09 >最新の環境でやると250スレッドに増えてるようです。
なるほど、そんな変更があったんですね。これは知りませんでした。
貴重な情報、ありがとうございます。(^^)

っと言うわけで、このサンプルは意味無しに・・・

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。