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";
}
Related
I faced with one interesting moment when working with multithreading.
I have two threads. In main thread I create layout and add to it control,in second thread I create another control and add to the same layout. It works fine, but second thread works a bit longer then main. So main should wait for second thread.I use for this AutoResetEvent and got DeadLock. Below I describe code what I use:
private static AutoResetEvent resetEvent = new AutoResetEvent(false);
private BackgroundWorker backgroundAdvancedViewWorker = new BackgroundWorker();
private delegate void ShowViewDelegate();
public void Run()
{
MainGeneralReportForm mainForm = ObjectFactory.GetOrCreateView<IMainGeneralReportForm>();
backgroundSimpleViewWorker.RunWorkerAsync(_mainForm);
GeneralReportFormatView formatView =
ObjectFactory.ShowView<IGeneralReportFormatView>()
resetEvent.WaitOne();
DoSomething(advancedSearchView);
}
private void backgroundAdvancedViewWorker_DoWork(object sender, DoWorkEventArgs e)
{
MainGeneralReportForm mainForm = e.Argument as MainGeneralReportForm;
if (mainForm!= null && mainForm.InvokeRequired)
{
mainForm.BeginInvoke(new ShowViewDelegate(() =>
{
advancedSearchView =
ObjectFactory.ShowView<IGeneralReportAdvancedSearchView>();
resetEvent.Set();
}));
}
}
}
If main thread doesn't wait for second thread, the application throws NullReferenceException.
Is exist any solution or workaround of this problem?
You block main thread by resetEvent.WaitOne(); and at the same time trying to schedule work item back to main thread with BeginInvoke (which indeed can't run as main thread is waiting).
Not sure what right fix would be, but blocking on main thread is not really an option.
Maybe some "state" field on the form may be enough. Or maybe running DoSomething(advancedSearchView); from BeginInvoke callback (instead of resetEvent.Set();).
Note: if you are on 4.5 you can consider using async/await instead of manual threading.
Execution Flow:
From main thread I invoked the new thread(Parallel thread), which is doing a long running process.
Parallel thread is updating the main thread UI.
I made my main thread to wait until parallel thread is complete.
I need a synchronization between two thread.
I need to use the result of parallel thread in main thread so I blocked main thread until parallel process complete.
Here is my code which is having issue,
please give suggestion to resolve the issue.
private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false);
private event EventHandler Workcompleted;
private void button1_Click(object sender, EventArgs e)
{
Workcompleted += Completed;
Thread thr = new Thread(UpdateUI);
thr.Start("");
_resetEvent.WaitOne();
// Logical operation dependent on parallel process final result
}
private void Completed(object sender, EventArgs args)
{
_resetEvent.Set();
}
private void UpdateUI(object txt)
{
for (int i = 0; i < 10; i++)
{
if (label1.InvokeRequired)
{
label1.Invoke(new ParameterizedThreadStart(UpdateUI), i.ToString());
}
else
{
label1.Text = (string)txt;
Thread.Sleep(100);
}
}
if (Workcompleted != null)
Workcompleted(this, new EventArgs());
}
I made my main thread to wait until parallel thread is complete.
And there you blocked yourself. Why did you start a new thread in the first place? To keep the UI responsive. And now your blocked it anyway. Do not block it. I don't know what you want to do while the thread is running, probably changing control states and resetting them when the thread is done, but what you don't want is blocking your UI thread. Stop that and find another way to achieve whatever you want to achieve.
It seems you are looking for a way to report progress in the UI during the course of the parallel operation and wait for the final result (synchronize) to do something with it.
This could easily be accomplished using Async/Await, without having to run manual threads, synchronization constructs or thread marshaling (for UI invocation) and most importantly without blocking the UI thread.
Here is an example of how to run a parallel operation, report progress back to the UI, update UI continuously and finally do something with the result when it is available.
private async void button1_Click(object sender, EventArgs e)
{
var progress = new Progress<int>(ShowProgressInUi);
var result = await Task.Run(() => DoParallelWorkAsync(progress));
// Do something with final result
label1.Text = result;
}
private void ShowProgressInUi(int progress)
{
label1.Text = string.Format("Progress: {0} % done...", progress);
}
private static async Task<string> DoParallelWorkAsync(IProgress<int> progress)
{
// This work is done in a separate thread.
// In this case a background thread (from the thread pool),
// but could be run on a foreground thread if the work is lengthy.
for (var i = 1; i <= 10; i++)
{
// Simulate workload
await Task.Delay(100);
progress.Report(i * 10);
}
return "All done";
}
public delegate void Action();
private void UpdateUI(object txt)
{
this.BeginInvoke((Action)(() =>
{
label2.Text = (string)txt;
}));
}
By using this code, we don't need to wait for another thread...
I'm thinking of a simple way of reacting on task finishing its work. I came up with the following solution (paste it to WinForms application with a single button to test):
public partial class Form1 : Form
{
private Thread thread;
public void DoFinishWork() {
// [4]
// UI thread - waiting for thread to finalize its work
thread.Join();
// Checking, if it really finished its work
MessageBox.Show("Thread state: " + thread.IsAlive.ToString());
}
public void DoWork() {
// [2]
// Working hard
Thread.Sleep(1000);
}
public void FinishWork() {
// [3]
// Asynchronously notifying form in main thread, that work was done
Delegate del = new Action(DoFinishWork);
this.BeginInvoke(del);
// Finalizing work - this should be switched
// at some point to main thread
Thread.Sleep(1000);
}
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
// [1]
// Schedule the task
ThreadStart start = new ThreadStart(DoWork);
// Schedule notification about finishing work
start += FinishWork;
thread = new Thread(start);
thread.Start();
}
}
This is meant to be a simple cancel scenario, so there will be only one thread, which will be running in parallel to the UI thread.
Is there a simpler (or more thread-safe) way of implementing this kind of notification for the Thread?
Please take into consideration two facts:
The only way i can terminate the thread is to Abort it (that's because I have no control over what is being done in the thread - 3rd party code)
Thus, I cannot use BackgroundWorker, because it only provides way of graceful termination.
Is there a simpler (or more thread-safe) way of implementing this kind of notification for the Thread?
Yes, use the TPL and let the framework worry about managing the thread
Task.StartNew(() => {
// do some stuff
}).ContinueWith((task) => {
// do some stuff after I have finished doing some other stuff
});
Or alternatively, since you are working with WinForms, use a BackgroundWorker and handle the RunWorkerCompleted event.
I mistook your notion of kill for cancel - there is no reliable way of actually killing a thread in .NET, even the documentation suggests that using Abort is more or less a gamble and gives absolutely no guarentees that the thread will actually be killed. Also, it will leave the thread and, as a consequence, the application in an unpredictable state so if you are willing to take that risk then that's up to you.
One alternative is to simply let the thread play out but just ignore the results, depending on the size of the task it might not be that big a deal.
Although you need Abort to kill the thread, you can still use the TPL. You could start that thread within the task, and wait for it as well as for an CancellationToken. When the task is cancelled before the thread finishes, you can call Abort on the thread.
It would look something like that:
// In your class:
ManualResetEvent threadFinished = new ManualResetEvent(false);
// In your calling function (button1_Click):
Task.Run( () => {
ThreadStart threadStart = new StreadStart(DoWork);
threadStart += () => { threadFinished.Set(); }
Thread thread = new Thread(threadStart);
threadFinished.Reset();
thread.Start();
WaitHandle waitCancel = cancellationToken.WaitHandle;
int waited = WaitHandle.WaitAny( new WaitHandle[]{ waitCancel, threadFinished } );
if (waited == 0 && cancellationToken.IsCancellationRequested)
thread.Abort();
else
thread.Join()
});
The whole point of the backgroundWorker is to update the UI after a time-consuming task. The component works as advertised in my WPF app.
However in my test, the callback is not invoked on the calling thread.
[Test]
public void TestCallbackIsInvokedOnClientThread()
{
var clientId = Thread.CurrentThread.ManagedThreadId;
int callbackThreadId = -1;
var manualEvent = new ManualResetEventSlim(false);
var someUIControl = new TextBox();
var bw = new BackgroundWorker();
bw.DoWork += (s,e) => e.Result = 5 ; // worker thread
bw.RunWorkerCompleted += (s, e) =>
{
try
{
callbackThreadId = Thread.CurrentThread.ManagedThreadId;
//someUIControl.Text = callbackThreadId.ToString();
manualEvent.Set();
}
catch (System.Exception ex)
{
Console.Out.WriteLine(ex.ToString());
}
};
bw.RunWorkerAsync();
if (!manualEvent.Wait(5000))
Assert.Fail("no callback");
Assert.AreEqual(clientId, callbackThreadId);
}
Result Message: Assert.AreEqual failed. Expected:<15>. Actual:<10>. callback not invoked on client Thread
What am I missing ?
In the Unit Test I see behavior like
------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9
In the Wpf App, this would be
MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1
Update:
With Justin's answer, made the following changes and now the test passes
Before creating the BackgroundWorker
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));
Instead of using a event for signalling between the threads, simulate a message pump
.
for (int i = 0; i < 3; i++)
{
control.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
Thread.Sleep(50);
}
The behavior is different dues to the different contexts that you are running under.
When you call bw.RunWorkerAsync(), the SynchronizationContext is captured. This is used to dispatch out the RunWorkerCompleted call.
Under WPF it will use DispatcherSynchronizationContext which will marshall the completed call back to the UI thread. Under the test, this marshalling is unnecessary so it remains on the background worker thread.
I belive that the calling thread must support messagepumping (mean, being STA apartment and having an associated Dispatcher) so the background worker can post the callback. If it does not, the background worker has no option but execute the callback in its own thread. If you want to test it, see this link.
I ran into a problem in my code where the user closing a window caused a save, that in turn used a BackgroundWorker to update the home window and it did not run the RunWorkerCompleted because the thread that started the BackgroundWorker had terminated when the window closed.
I had to change the closing window's save run in the home window's context so that after the BackgroundWorker completed, it had a thread to return to.
In my case I am using Windows Forms and controls don't have a Dispatcher property (see the answer in no definition for dispatcher).
Gishu's solution works as well if we use Dispatcher.CurrentDispatcher instead of the one in the control.
On test initialisation:
// I am using a field Dispatcher _dispatcher
_dispatcher = Dispatcher.CurrentDispatcher;
And then when waiting for the background task to be completed:
_dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Thread.Sleep(50);
I connect to a webserive. While the webservice is connected i want to have a waiting form with an animated gif inside of it. The waiting form is correctly displayed but the animated give is not animated it is fixed.
Can anybody help me. I have already tried : DoEvents but the gif is still not animated.
// Create the new thread object
Thread NewThread = new Thread(new ThreadStart(RunThread));
// Start the new thread.
NewThread.Start();
// Inform everybody that the main thread is waiting
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
waitingDialog.Activate();
Application.DoEvents();
// Wait for NewThread to terminate.
NewThread.Join();
// And it's done.
waitingDialog.Close();
MessageBox.Show("Upload erfolgreich erledigt.", "Upload Erfolgreich",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
public void RunThread()
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
}
Don't call Join on a thread from within the UI thread. Instead, disable any controls you don't want to act on until the task has completed (e.g. buttons) and then call back into the UI thread when the operation has completed - so move the "And it's done" code into a new method which is invoked at the end of the operation. If you're using .NET 4, I'd suggest using the TPL for this, as it makes it easier to represent "a task which is in progress" and to add a continuation to it. (It's also a good start for what will become the idiomatic way of doing async operations in .NET 4.5.)
The problem is coming from your join. join is synchronous, so basically you are making your UI wait till the thread finishes its work.
You want to use a callback function to come back to your UI.
Edit : ive been skeetified
You problem is here:
NewThread.Join();
This blocks the UI thread until NewThread ends.
Here's one way to do it:
private myDelegate;
// ...
myDelegate = new Action(RunThread);
myDelegate.BeginInvoke(new AsyncCallback(MyCallback),null);
// You RunThread method is now running on a separate thread
// Open your wait form here
// ...
// This callback function will be called when you delegate ends
private void MyCallback(IAsyncResult ar)
{
myDelegate.EndInvoke(ar);
// Note this is still not the UI thread, so if you want to do something with the UI you'll need to do it on the UI thread.
// using either Control.Invoke (for WinForms) or Dispatcher.Invoke (for WPF)
}
Thread.Join is a blocking call that does not pump messages so that is your problem. It is typically advised to avoid calling any kind of synchronization mechanism that causes the UI thread to block.
Here is a solution using the Task class and the Invoke marshaling technique.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
Task.Factory.StartNew(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
waitingDialog.Invoke(
(Action)(() =>
{
waitingDialog.Close();
}));
});
}
Here is a solution using a raw Thread.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
var thread = new Thread(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
waitingDialog.Invoke(
(Action)(() =>
{
waitingDialog.Close();
}));
});
thread.Start();
}
C# 5.0 makes this kind of pattern even easier with its new async and await keywords1.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
await Task.Run(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
});
waitingDialog.Close();
}
1Not yet released.