2

ユーザー。答えが見つからない問題に直面しています。私は(C#で)スレッディングに慣れていないので、この問題に遭遇しました。エフェクト付きのこの画像エディターを持っていますが、実行が遅すぎるため、スレッドに分割しようとしました。問題は、彼が常に効果のリストの最後の項目で「CreatePreview」コマンドを実行することです。「Black/White」、「Sature」、「GreenFilter」のエフェクトを有効にすると、greenfilter を使用して 3 つのプレビューを作成しようとします。

誰かがこの問題で私を助けてくれますか?

private void CreatePreviews(string fileName, List<IEffect> effects)
{
    List<Task> tasks = new List<Task>();
    foreach (var effect in effects)
    {
        //previews.Add(effect, CreatePreview(fileName, effect));
        Task task = new Task(delegate()
        {
            string result = CreatePreview(fileName, effect);
            Dispatcher.BeginInvoke(new Action(
            delegate()
            {
                ShowPreview(result, effect.DisplayName);
            }));

        });
        task.Start();
    }
}
4

4 に答える 4

5

今はテストできませんが、問題はループ変数を閉じていることだと確信しています。

ループ変数のコピーを取得し、代わりにそれを閉じます。

foreach (var effect in effects)
{
    var effectCopy = effect;

    //previews.Add(effectCopy, CreatePreview(fileName, effectCopy));
    Task task = new Task(delegate()
    {
        string result = CreatePreview(fileName, effectCopy);
        Dispatcher.BeginInvoke(new Action(delegate()
        {
            ShowPreview(result, effectCopy.DisplayName);
        }));
    });

    task.Start();
}

(または、反復ごとに変数の新しいコピーを自動的に閉じる C#5 を待ちます。)

于 2012-05-21T23:25:24.000 に答える
1

デリゲート内の変更されたクロージャーへのアクセスを防ぐために、現在の変数をループ内の変数に保存する必要がありますeffect。つまり、すべてのデリゲートがループ変数にアクセスし、最終的にループする最後の要素の値を持ちます。したがって、すべてのタスクは最後の効果で実行されます。それを防ぐには:

private void CreatePreviews(string fileName, List<IEffect> effects)
{
    List<Task> tasks = new List<Task>();

    foreach (var effect in effects)
    {
        var mcEffect = effect;

        Task task = new Task(delegate()
            {
                string result = CreatePreview(fileName, mcEffect);
                Dispatcher.BeginInvoke(new Action(
                delegate()
                {
                    ShowPreview(result, effect.DisplayName);
                }));
            });

        task.Start();
    }
}

mc変更された閉鎖に注意するために接頭辞を付けるのが好きです。

于 2012-05-21T23:24:39.360 に答える
1

スレッドが実際に効果を評価する前にループ反復子がすべての変更をキューに入れているために、実際に評価されたときの値が変わらないように、デリゲートは効果の値のローカル コピーを作成する必要があります。

foreach(var effect in effects)
{
    var localEffect = effect;
    var task = new Task(()=>
        {
            var result = CreatePreview(fileName, localEffect);
            Dispatcher.BeginInvoke(()=> ShowPreview(result, localEffect.DisplayName));
        });
    task.Start();
}

これにより、個々のスレッドが効果の作成時の値で適切に閉じられます。これは、匿名デリゲートがバックグラウンドで隠しクラスを作成する方法によるものです。

あなたが作成したものがレキシカル クロージャーを作成しなかった理由については、この記事を参照して ください。

于 2012-05-21T23:29:42.890 に答える
0

マルチスレッドは非常に複雑な問題であり、多くの問題が発生する可能性があります。

Task Parallel Library の記事 (または本) を必ず 1 つか 2 つ読んでください。

TPL を使用した潜在的により正確なバージョンは、次のようになります。

Parallel.ForEach(effects, currentEffect =>
{
    string result = CreatePreview(fileName, currentEffect );
    ShowPreview(result, effect.DisplayName);
}

PS。この場合のベスト プラクティスは、各フィルタリング操作を実際に並列化することです (または、GPU にオフロードすることをお勧めします)。

于 2012-05-21T23:28:16.577 に答える