C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[6]: delegateの非同期呼び出し


(過去ログ 134 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■78886 / inTopicNo.1)  delegateの非同期呼び出し
  
□投稿者/ 案山子 (1回)-(2016/02/23(Tue) 09:48:08)

分類:[C#] 

Visual Studio 2012 + .NET Framework 4.5 で C# を使用しています。
.NETのバージョンを4.5に挙げたのは最近なのでC# 5.0について勉強しつつ
業務でC#コードをいじっている者です。はじめまして。

本日思い切ってこちらの C# wizard な方々に質問させていただくのは
delegate 登録された複数の method を非同期で呼び出す方法
を探しているためです。

サンプルですが以下のようなことをしたいです。
(説明に関係ない部分は省略しています)


public class Sample
{
    // 複数のclassからmethodが登録されている状態
    // 登録されるmethodは互いに干渉し合わない重い処理
    public Action<string> Work;

    private bool SampleWork(string input)
    {
        // いろいろな処理を同期実行した後に

        if(Work != null)
        {
            // 登録された method 全部を呼び出す
            Work(input);
        }
    }
}

class A
{
    void Work(string input)
    {
         // 重い処理の例として
         Random random = new Random((int)(DateTime.Now.Ticks % Int32.MaxValue));
         while(random.Next() != input.Length) { };
    }
}

class B
{
    void Work(string input) { /* 何か重い処理 */ }
}

class C
{
    void Work(string input) { /* 何か重い処理 */ }
}


例えば上記class SampleのWorkに
class A, class B, class CのWorkが登録されていた場合
単純にWorkを呼び出すだけでは
A.Work → B.Work → C.Work (順不問)
と同期で呼び出されると思っています。

私がやりたいのは
A.Work, B.Work, C.Work を同時にキックしたい
(A.Workの処理完了を待たずにB.WorkやC.Workの処理を開始したい)
ということです。

私が担当するのはclass Sampleのみなので
class A, class B, class Cはできれば変更したくありません。
(他に手がないのであれば, いじれなくはありません)
また態々WorkerThreadを使うほどでもない気がしています.

async, awaitというキーワードの説明を読んでみたのですが
例がbutton_Clickedイベントの処理のせいか
上記でどう使えば良いのか/使えるのか判断が付きませんでした。

お手数をおかけしますが
ご助言頂きたくよろしくお願いいたします。

引用返信 編集キー/
■78887 / inTopicNo.2)  Re[1]: delegateの非同期呼び出し
□投稿者/ Hongliang (410回)-(2016/02/23(Tue) 10:26:31)
簡単にやるのであれば、
Parallel.ForEach(this.Work.GetInvocationList().Cast<Action<string>>(), _ => _(input));
とか。
登録されているデリゲートが多い場合の、後ろの方の要素の起動タイミングはやや癖がありますが。
引用返信 編集キー/
■78889 / inTopicNo.3)  Re[2]: delegateの非同期呼び出し
□投稿者/ 案山子 (3回)-(2016/02/23(Tue) 11:36:59)
No78887 (Hongliang さん) に返信
> 簡単にやるのであれば、
> Parallel.ForEach(this.Work.GetInvocationList().Cast<Action<string>>(), _ => _(input));
> とか。

たった一行で!できました!!ありがとうございます!
Parallelクラスを知りませんでした、勉強不足を痛感しています...。

> 登録されているデリゲートが多い場合の、後ろの方の要素の起動タイミングはやや癖がありますが。

「癖」とは何でしょうか。
最初に呼び出されるものと最後に呼び出されるものとの間に時間差が生じる点については
デリゲートの処理内容から問題なしと判断しております。
それ以外に注意すべきものがあればご教授頂ければ幸いです。
解決済み
引用返信 編集キー/
■78890 / inTopicNo.4)  Re[3]: delegateの非同期呼び出し
□投稿者/ 案山子 (4回)-(2016/02/23(Tue) 11:37:06)
2016/02/23(Tue) 11:37:52 編集(投稿者)

送信ミスしました。すみません。
解決済み
引用返信 編集キー/
■78891 / inTopicNo.5)  Re[4]: delegateの非同期呼び出し
□投稿者/ Hongliang (411回)-(2016/02/23(Tue) 11:55:11)
Parallelは既定では組み込みのスレッドプール(System.Threading.ThreadPool)を使用します。
ThreadPoolでは最小スレッド数と最大スレッド数が定義されています(プログラムから変更可能)。
ThreadPoolに要求が出されると、ThreadPoolが扱っている処理が最小スレッド数に満たない間は、直ちに処理がスレッドに割り当てられ開始されます。
しかし最小スレッド数に達した後にまだ割り当て待ち処理が残っている場合、それらは一定時間ごと(500msとかそういう感じ)に新たにスレッドが起こされて割り当てられるようになります。
またThreadPoolはプロセス全体で共有されるため、他にThreadPoolを使用している物がある場合、このスレッド割り当て処理が相互に影響する可能性があります(Parallel.ForEachで沢山割り当て待ち処理ができちゃうと、他で割り当てた処理の開始が思いの外遅くなるとか)。

あと、「重い処理」がCPUコア1つを使い切るような処理の場合、PCが持つCPUコア数以上のスレッドが起こされて結果的に全体で見るとかえって遅くなるような可能性もあります。
解決済み
引用返信 編集キー/
■78894 / inTopicNo.6)  Re[5]: delegateの非同期呼び出し
□投稿者/ なちゃ (103回)-(2016/02/23(Tue) 12:54:37)
まあスレッド数に関しては、基本的CPU数とCPU使用率でうまく調整してくれるので、CPU空き空きの処理がたくさんとかでなければ、スレッドの起動などで困った事態になることはあまりないでしょう。
明らかにスレッド数が足りないなら、あらかじめ最小スレッド数を増やした方が良いかも知れません。

あとは、各処理に重い軽いがある場合、投入順序がよろしくないと全体の処理時間が長くなる可能性があります。
基本は重い処理(長時間かかる処理)を先に投入するのが望ましいです。
まあなかなかそんな調整は出来ないことも多いですが。
引用返信 編集キー/
■78895 / inTopicNo.7)  Re[5]: delegateの非同期呼び出し
□投稿者/ 案山子 (5回)-(2016/02/23(Tue) 12:54:55)
No78891 (Hongliang さん) に返信
解説ありがとうございました。

Parallelだからといって特殊なことはなく、
ただコード上一行でたくさんのスレッドが生成されうるので
「うっかり」やらないよう留意する必要がある
と理解しました。

解決済み
引用返信 編集キー/
■78978 / inTopicNo.8)  Re[3]: delegateの非同期呼び出し
□投稿者/ 案山子 (6回)-(2016/02/27(Sat) 09:18:42)
こんにちは、案山子です。

Hongliangさんにご教授頂いたParallel.ForEachで
やりたいことができると思ったのですが...
今更Parallel.ForEachでは駄目なことがわかりました.

実は以下のようなことを実現しないといけないようです.
(サンプル改変しました)

public class SampleNew
{
    // 複数のclassからmethodが登録されている状態
    // 登録されるmethodは互いに干渉し合わない重い処理
    public Action<string> Work;

    private bool SampleWork(string input)
    {
        // いろいろな処理を同期実行した後に

        if(Work != null)
        {
            // 登録された method 全部を呼び出す ← 教えていただいた方法
            Parallel.ForEach(this.Work.GetInvocationList().Cast<Action<string>>(), _ => _(input));
        }

        // 全部呼び出したらすぐに次の処理を実行したい    ←NEW!!
        I_have_next_work();
    }
}

class A
{
    void Work(string input)
    {
         // 重い処理の例として
         Random random = new Random((int)(DateTime.Now.Ticks % Int32.MaxValue));
         while(random.Next() != input.Length) { };
    }
}

class B
{
    void Work(string input) { /* 何か重い処理 */ }
}

class C
{
    void Work(string input) { /* 何か重い処理 */ }
}


Parallel.ForEachは呼び出した method がすべて完了するまで待機してしまい
次の処理はすぐには開始できません.
ダメ元でSampleWorkにasyncを付けてみましたが, 付けただけではやっぱりダメでした.
何か手をご存知の方がいらっしゃいましたら、ご助言いただきたくよろしくお願いいたします。

引用返信 編集キー/
■78979 / inTopicNo.9)  Re[4]: delegateの非同期呼び出し
□投稿者/ Hongliang (417回)-(2016/02/27(Sat) 09:26:58)
GetInvocationListの結果をforeachでそれぞれTask.Runするとか、Parallel.ForEachをTask.Runするとか。
引用返信 編集キー/
■78980 / inTopicNo.10)  Re[5]: delegateの非同期呼び出し
□投稿者/ なちゃ (108回)-(2016/02/27(Sat) 11:04:35)
んー、これならForEach部分は並列じゃなく普通に同期で単純にループして、それぞれをTask.Runなりでタスク投入でいいと思います。
引用返信 編集キー/
■78981 / inTopicNo.11)  Re[6]: delegateの非同期呼び出し
□投稿者/ 案山子 (7回)-(2016/02/27(Sat) 12:39:04)
No78979 (Hongliang さん)
No78980 (なちゃ さん)

返信ありがとうございます。
サンプルコードで書くとすればこんな感じでOKでしょうか。

Tasks.GetInvocationList().Cast<Action>.ToList().ForEach(t => Task.Run(t));

試行して見たところ
Actionに登録した method がそれぞれの完了を待たずに処理を開始し
かつ
呼び出し側の次の処理も上記完了を待たずに実行できました。
ありがとうございます。

見た目的にも
もっとスマートなやり方をご存知でしたらご教授いただければ幸いです。

蛇足:
呼び出した処理を待っても構わない箇所では前に教えていただいたParallelで幸せになっております。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -