问题
I have multiple threads accessing a method.
That method updates the progress bar on the form as the threads execute.
That method updates the progress bar on my windows form showing the progress of the processed files as the threads are completing them.
How would you go about managing that method to avoid deadlocks. Im having trouble locking it, dont know if im doing it wrong, or if i should lock it in this case.
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);
}
What happens is, that at the last line, it breaks telling me that "This operation has already had OperationCompleted called on it and further calls are illegal."
I take it that im opening something that has already been disposed by the first thread or something like that.
Is this a good way to go about using the progress bar with multiple threads (do i have the right idea or principle), or is there just a small error in my code?
How I want to make this question valuable for other people is to answer with this question how global variables can be accessed inside a method that's being accessed by multiple threads. since i take it thats the direction this question is going in.
回答1:
It looks like you're using the BackgroundWorker class, which I'm not familiar with.
But you're also asking for a "generic" design for using multiple threads and ProgressBar. This example would be my way for updating a ProgressBar from a number of threads.
First, you need a public delegate (probably in some helper-class)
//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;
}
}
In each worker class you must register the delegate
// event for reporting progress
private event ProgressChangedHandler ProgressChanged;
// register Eventhandler via dependency injection or own method
public void RegisterDelegate(ProgressChangedHandler progressChangedHandler)
{
ProgressChanged += progressChangedHandler;
}
In the UI class which holds the ProgressBar you need a method for updating the progress. But every call to that method is executed in the thread of the caller (which can be problematic with the not-threadsafe UI).
To avoid this, you can call the dispatcher, so that every raised event will be handled in your UI thread and not the worker thread.
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;
}
}
This method you can pass to the worker as delegate
worker.RegisterEventHandler(StatusUpdate);
Finally you can call the delegate via an event from every thread
public void DoVeryHardWork()
{
// do stuff
ProgressChanged(this, new ProgressEventArgs(progress, "some message"));
}
来源:https://stackoverflow.com/questions/12812725/method-accessed-by-more-than-one-thread-in-c-sharp-using-progressbar