1

メソッドにアクセスする複数のスレッドがあります。

このメソッドは、スレッドの実行時にフォームの進行状況バーを更新します。

そのメソッドは、スレッドがファイルを完了するにつれて、処理されたファイルの進行状況を示す Windows フォームの進行状況バーを更新します。

デッドロックを回避するために、そのメソッドをどのように管理しますか。ロックに問題があります。間違っているのか、この場合はロックする必要があるのか​​ わかりません。

public void myWorkerClass()
        {
            int amountToScan = 4;

            lblcount.BeginInvoke(
               ((Action)(() => lblcount.Text = "Files Checked " + atPDFNumber.ToString() + " of " + amountToScan.ToString())));

            decimal percentageComplete = ((decimal)atPDFNumber / (decimal)amountToScan) * 100;

            backgroundWorker1.ReportProgress((int)percentageComplete);

        }

何が起こるかというと、最後の行で、「この操作はすでに OperationCompleted が呼び出されており、それ以上の呼び出しは無効です」というメッセージが表示されます。

私は、最初のスレッドまたはそのようなものによって既に破棄されたものを開いていると思います。

これは、複数のスレッドで進行状況バーを使用する良い方法ですか (正しいアイデアまたは原則を持っていますか)、それとも私のコードに小さなエラーがありますか?

この質問を他の人にとって価値のあるものにしたいのは、複数のスレッドによってアクセスされているメソッド内でグローバル変数にアクセスする方法について、この質問に答えることです。私はそれがこの質問の方向性だと思っています。

4

1 に答える 1

2

私がよく知らない BackgroundWorker クラスを使用しているようです。

しかし、複数のスレッドと ProgressBar を使用するための「一般的な」設計も求めています。この例は、多数のスレッドから ProgressBar を更新する私の方法です。

まず、パブリック デリゲートが必要です (おそらく一部のヘルパー クラスで)。

//is called when the worker-progress is changed
public delegate void ProgressChangedHandler(object sender, ProgressEventArgs e);

// some own EventArgs
public class ProgressEventArgs : EventArgs
{
    public int Percentage { get; private set; }

    public string Message { get; private set; }

    public ProgressEventArgs(int percentage, string message)
    {
        Percentage = percentage;
        Message = message;
    }
}

各ワーカー クラスで、デリゲートを登録する必要があります

// event for reporting progress
private event ProgressChangedHandler ProgressChanged;

// register Eventhandler via dependency injection or own method
public void RegisterDelegate(ProgressChangedHandler progressChangedHandler)
{
    ProgressChanged += progressChangedHandler;
}

ProgressBar を保持する UI クラスでは、進行状況を更新するためのメソッドが必要です。ただし、そのメソッドへのすべての呼び出しは、呼び出し元のスレッドで実行されます (スレッドセーフでない UI では問題になる可能性があります)。

これを回避するには、ディスパッチャーを呼び出して、発生したすべてのイベントがワーカー スレッドではなく UI スレッドで処理されるようにします。

private void StatusUpdate(object sender, ProgressEventArgs args)
{    
    if (Dispatcher.Thread.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
    {
        // call from a worker thread
        var statusUpdateDelegate = new ProgressChangedHandler(this.StatusUpdate);

        Dispatcher.Invoke(statusUpdateDelegate, DispatcherPriority.Normal, sender, args);
    }
    else
    {
        // direct call from the UI thread
        lblProgress.Content = args.Message;
        pbProgress.Value = args.Percentage;
    }
}

このメソッドは、デリゲートとしてワーカーに渡すことができます

worker.RegisterEventHandler(StatusUpdate);

最後に、すべてのスレッドからイベントを介してデリゲートを呼び出すことができます

public void DoVeryHardWork()
{
    // do stuff
    ProgressChanged(this, new ProgressEventArgs(progress, "some message"));
}
于 2012-10-10T13:18:32.893 に答える