I have the following code which updates my progress bar and status bar from a backgroundworker. I run the same backgroundworker twice. The first time I run it I call it from the MainWindow constructor it works fine. At the end of the constructor I setup a timer to call the method every so often.
System.Threading.TimerCallback timerCallback = new System.Threading.TimerCallback(RefreshWebDataTimer);
timer = new System.Threading.Timer(
timerCallback, null,
Dictionary.MS_TIMER_FIRSTREFRESH_PERIOD,
Dictionary.MS_TIMER_REFRESH_PERIOD);
When calling it from the timer I get the following error:
A first chance exception of type 'System.InvalidOperationException'
occurred in WindowsBase.dll Additional information: The calling thread
cannot access this object because a different thread owns it.
I added some debug and indeed the Dispatcher Thread is on a different thread from the timer and the same thread from the original run.
private void backgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
System.Diagnostics.Debug.Print("Current Thread: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
System.Diagnostics.Debug.Print("Dispatcher Thread: {0}", progressBar.Dispatcher.Thread.ManagedThreadId);
this.progressBar.Visibility = Visibility.Visible;
this.progressBar.Value = e.ProgressPercentage;
if (e.UserState != null)
{
this.statusBar.Text = e.UserState.ToString();
}
}
Current Thread: 22
Dispatcher Thread: 7
I was under the impression that the ProgressChanged and RunWorkerCompleted events always ran on the main UI thread in order to solve this problem and be able to do UI updates. Apparently, I misunderstand what is going on here.
I updated my solution to use the Dispatcher as follows:
private void backgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
progressBar.Dispatcher.BeginInvoke(new OneArgIntDelegate(updateProgressBar), e.ProgressPercentage);
if (e.UserState != null)
{
progressBar.Dispatcher.BeginInvoke(new OneArgStrDelegate(updateStatusBar), e.UserState.ToString());
}
}
private void updateStatusBar(string Text)
{
this.statusBar.Text = Text;
}
private void updateProgressBar(int ProgressPercentage)
{
this.progressBar.Visibility = Visibility.Visible;
this.progressBar.Value = ProgressPercentage;
}
This solution worked but I thought the whole point of the BackgroundWorker was that I didn't have to do this. Can someone explain my incorrect assumption and what is really going on. Is there a way to do this WITHOUT the dispatcher by setting up the timer differently?
Thanks,
Harrison
I was under the impression that the ProgressChanged and
RunWorkerCompleted events always ran on the main UI thread in order to
solve this problem and be able to do UI updates. Apparently, I
misunderstand what is going on here.
The BackgroundWorkers ProgressChanged is called back to the thread that owns the BackroundWorker, this is not aways the UI thread, When you create the BackgroundWorker the second time it is being created on another thread so the ProgressChanged will be invoked on the thread that created the BackgroundWorker in this case the timer thread.
First Call: UIThread => BacgroundWorker => Progress => UIThread
SecondCall: TimerThread => BacgroundWorker => Progress => TimerThread
You can ether invoke the RefreshWebDataTimer from the Timer to the UI thread or use a DispatcherTimer to ensure the RefreshWebDataTimer is called on the UI thread.
Option 1:
timer = new System.Threading.Timer(
(o) => Dispatcher.Invoke((Action)RefreshWebDataTimer),
null,
Dictionary.MS_TIMER_FIRSTREFRESH_PERIOD,
Dictionary.MS_TIMER_REFRESH_PERIOD);
Option 2:
timer = new DispatcherTimer(
TimeSpan.FromSeconds(1),
DispatcherPriority.Background,
(s, e) => RefreshWebDataTimer(),
Dispatcher);
Related
According to answer for this question Why Thread.Join() DOES NOT hang my application when called on UI thread? thread.Join should not hang UI if it called from STA thread. I used the same code as in linked question
private void button1_Click(object sender, EventArgs e)
{
string retValue = "";
Thread thread = new Thread(
() =>
{
retValue = LongRunningHeavyFunction();
});
thread.Start();
thread.Join();
button1.Text = retValue;
}
private string LongRunningHeavyFunction()
{
Thread.Sleep(5000);
return "Done";
}
Method Main in class Program marked as [STAThread]. But when I press button UI is freezed, I can't drag window etc. I'm confused. Am I missed something? Why UI is freezed in my case?
Thread.Sleep causes UI to freeze.
If you want to wait for a while in LongRunningHeavyFunction(), use a timer object.
Here is an example, how to use timer:
How to use a timer to wait?
thread.Join() tells the current thread to await thread. In this case, the current thread is the GUI thread, and you're telling it to await the worker thread. As a result, your GUI thread does nothing until the worker thread completes. And since the GUI thread is doing nothing, it isn't handling normal GUI activities, causing the freeze.
The solution is to not block your GUI. Instead, run your long-running process without awaiting it, such that your GUI thread can keep responding to the user. Then, once the long-running process does complete, use the GUI dispatcher to call back to set the result.
The code might look something like this:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(
() =>
{
// Perform work
var retValue = LongRunningHeavyFunction();
// Call the GUI thread
button1.Dispatcher.BeginInvoke(() =>
{
// .Dispatcher called the GUI thread.
// This code happens back in the GUI thread once the
// worker thread has completed.
button1.Text = retValue;
});
});
thread.Start();
}
private string LongRunningHeavyFunction()
{
Thread.Sleep(5000);
return "Done";
}
Can someone explain why invoking Thread.Sleep from BackgroundWorker blocks its execution. Invoke should cause the delegate to be executed on the UI thread and background thread should continue with execution. But that does not happen - why?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BackgroundWorker bgrw = new BackgroundWorker();
bgrw.DoWork += new DoWorkEventHandler(bgrw_DoWork);
bgrw.RunWorkerAsync();
}
void bgrw_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine(DateTime.Now);
this.Invoke(new Action(() => { Thread.Sleep(2000); })); //should be executed on the UI thread
Console.WriteLine(DateTime.Now); // This line is executed after 2 seconds
}
}
It's a rather simple explanation. Invoke is a blocking call. If you want to queue work on the UI message loop asynchronously, use BeginInvoke instead:
Executes the specified delegate asynchronously on the thread that the
control's underlying handle was created on.
void bgrw_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine(DateTime.Now);
this.BeginInvoke(new Action(() => { Thread.Sleep(2000); }));
Console.WriteLine(DateTime.Now);
}
Note your code, as currently constructed makes no sense. I'm assuming you're using this for testing purposes.
I have a multithreaded program. I want start the new BackgroundWorker and pause the current thread. Then I want resume previous thread in the new BackgroundWorker.
I am programming in C#.
I have a big project and cannot put my code here.
You can use AutoResetEvent and use WaitOne to hold the parent thread. Call AutoResetEvent.Set method from spawned thread to resume the execution of parent (main) thread.
childThread.Start();
autoResetEvent.WaitOne();
In child (spawned thread)
private void SpawnedThread()
{
//your code
autoResetEvent.Set(); //will resume the execution after WaitOne(), may be under some condition.
}
You can use overloaded version of WaitOne to give the maximum wait time. The execution will resume of Set method is not being called until the give time.
Here is my sample code for you! I'm not so sure it useful for your project, but this is my idea. hope helpful.
BackgroundWorker bwExportLogFile = new BackgroundWorker();
private void ExportLogFile() {
bwExportLogFile.DoWork += bwExportLogFile_DoWork;
bwExportLogFile.RunWorkerCompleted += bwExportLogFile_RunWorkerCompleted;
bwExportLogFile.ProgressChanged += bwExportLogFile_ProgressChanged;
bwExportLogFile.RunWorkerAsync();
bwExportLogFile.WorkerReportsProgress = true;
bwExportLogFile.WorkerSupportsCancellation = true;
}
void bwExportLogFile_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if(some thing is true here){
bw.CancelAsync();
}
}
So when you want to run thread in BackgroundWorker again just call this:
bwExportLogFile.RunWorkerAsync();
Try to set WorkerSupportsCancellation = true and in ProgressChanged event you can do like this:
BackgroundWorker bw = sender as BackgroundWorker;
bw.CancelAsync();
bw.RunWorkerAsync();
I've been trying to learn more about asynchronous tasks and threading but not making a ton of headway.
I'm trying to load an "Engine" type of thread that will run in the background upon launch and be able to access the UI Thread to update variables, without hanging the UI Thread.
In the below code, Engine is called, and a Ticker object is created which holds the current value of (Litecoin/USD) called Last, also holds several other values that would be useful. This code successfully assigns the current value to label1.text. I don't necessarily need code but what approach would I take to create a ticker object in the background every second and update the UI thread with each new Ticker objects values.
Is this a good case for a background worker?
private void Form1_Load(object sender, EventArgs e)
{
Engine();
}
private void Engine()
{
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD:" + ltcusd.Last;
}
EDIT:
If I do the following, label1 throws an InvalidOperationException due to a Cross-thread operation attempt (label1 in the UI thread).
private void Form1_Load(object sender, EventArgs e)
{
var t = Task.Factory.StartNew(() => Engine());
t.Start();
}
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
}
Using async/await, the simplest way of getting an "asynchronous" sort of API is to invoke a new task. It's not great, but it'll make things simpler. I would probably create a new class which basically wrapped all the BtceApi methods in tasks:
public class BtceApiAsync
{
public Task<Ticker> GetTickerAsync(BtcePair pair)
{
return Task.Run(() => BtceApi.GetTicker(pair));
}
// etc
}
Then you can use a timer which fires once per second, which will start off a new task and update the UI appropriately:
// Keep a field of type System.Windows.Forms.Timer
timer = new Timer();
timer.Interval = 1000;
timer.Tick += DisplayTicker;
timer.Start();
...
private async void DisplayTicker(object sender, EventArgs e)
{
Ticker ticker = await BtceApiAsync.GetTickerAsync(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
Note that this doesn't mean the screen will be updated once per second... there will be a new task started once per second, and as soon as each task completes, the UI will be updated.
The use of await here - from an async method started on the UI thread - means you don't need to worry about using the UI; the whole async method will execute on the UI thread, even though the fetch itself happens in a different thread.
You can try ContinueWith to update the Label at the end of the task. If you want to update it event before the task ends then raise an event which is registered by on the UI thread. The event can then update the label.
I suppose this is Windows Forms. You could do it "old school style" and set the label text on the UI thread, and you can do that by passing delegate to the BeginInvoke or Invoke method.
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
UpdateText("LTC/USD: " + ltcusd.Last);
}
}
private void UpdateText(string text)
{
//Inspect if the method is executing on background thread
if (InvokeRequired)
{
//we are on background thread, use BeginInvoke to pass delegate to the UI thread
BeginInvoke(new Action(()=>UpdateText(text)));
}
else
{
//we are on UI thread, it's ok to change UI
label1.Text = text;
}
}
I am trying to update an ObservableCollection that is data bound to the UI. I know that to do this I need to use Dispatcher and BeginvInvoke(), and to make it so that the UI doesn't freeze up when I do so, using a BackgroundWorker is a good way to go about it. In any event, I have all this, compiled and running, but nothing happens. I need to update the UI every 2 minutes or so, so I am also using a DispatcherTimer
This works, because DispatcherTimer is part of Dispatcher, but freezes the UI:
DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();
private void dispTimer_Tick(object sender, EventArgs e)
{
PartialEmployees.Clear();
}
So, using the BackgroundWorker I pieced together this:
DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();
private void dispTimer_Tick(object sender, EventArgs e)
{
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += DoWork;
_worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke( new Action(()=>
{
PartialEmployees.Clear();
}));
}
But nothing happens to the UI. What am I missing/not doing correctly?
You have two problems:
When you use Dispatcher.CurrentDispatcher from the background thread, it is getting the background thread's Dispatcher, not the UI thread's Dispatcher.
From your description I gather that your PartialEmployees.Clear() method takes significant time to execute and you want to avoid locking the UI thread during the execution. However, having a BackgroundWorker invoke PartialEmployees.Clear() on your UI thread will have the same effect as using the DispatcherTimer, so you need a different solution than the one you are going for.
If you only want to fix the Dispatcher.CurrentDispatcher problem, just store the current Dispatcher in a local variable like this:
private void dispTimer_Tick(object sender, EventArgs e)
{
var uiDispatcher = Dispatcher.CurrentDispatcher;
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += (sender, e) =>
uiDispatcher.BeginInvoke(new Action(() =>
{
PartialEmployees.Clear();
}));
_worker.RunWorkerAsync();
}
This will cause your UI change to work but it will still lock up the UI during the change, exactly as if you had not used BackgroundWorker. The reason for this is:
The DispatcherTimer fires, executing on the UI thread. All it does (dispTimer_Tick) is start a BackgroundWorker and then exit.
The BackgroundWorker executes on its own therad. All it does is schedule a Dispatcher callback and then exit.
The Dispatcher callback executes on the UI thread again. It calls PartialEmployees.Clear() which takes a while, locking up your UI thread while it executes.
So your behavior is the same as if the DispatcherTimer callback had called PartialEmployees.Clear() directly: In each case the time-consuming operation is executed on the UI thread.
The reason for the lockup is that any time you do a large piece of work on the UI thread you will get a momentary lockup while it runs. The solution is to break your work into smaller portions and do them one at a time, either from a DispatcherTimer or a BackgroundWorker. In your case, examine the code for PartialEmployees.Clear() to see if it can be done incrementally.
The problem here is that you're using the method Dispatcher.CurrentDispatcher from the back ground thread. What you need is the Dispatcher instance for the UI thread.
_worker.DoWork += delegate { DoWork(Dispatcher.CurrentDispatcher); };
...
private void DoWork(Dispatcher dispatcher) {
dispatcher.BeginInvoke(new Action(() => {
PartialEmployees.Clear();
});
}
I dont think you need the background work, as BeginInvoke on the Dispatcher runs on a Threadpool thread.
something like this should work, and is more succinct
DispatcherTimer dispTimer = new DispatcherTimer
{Interval = TimeSpan.FromSeconds(45)};
dispTimer.Tick += (o,e) => Dispatcher.CurrentDispatcher
.BeginInvoke((Action)PartialEmployees.Clear);
dispTimer.Start();