I'm beginner in c#,and i write this code for start the new thread:
Thread workerThread = new Thread(DoWork);
workerThread.Priority = ThreadPriority.Highest;
workerThread.Start();
in the up thread process some thing and show into the chart,every thing is okay,but when run and finish DoWork method,chart control visible set to false automatically!,my DoWork method is:
public void DoWork()
{
//.....some process and show into the process result into the chart
chart1.Visible = true;//this code not run
}
how can solve that?
You do not have access to UI elements from a different thread.
For Winforms:
How to update the GUI from another thread in C#?
For WPF:
Change WPF controls from a non-main thread using Dispatcher.Invoke
chart1.Dispatcher.Invoke(() =>chart1.Visible = true);
Change your Dowork method signature to accept object as parameter and pass Synchronization context to it:
void DoWork(object o)
{
SynchronizationContext cont = o as SynchronizationContext;
// your logic gere
cont.Post(delegate
{
// all your UI updates here
}, null);
}
Thread workerThread = new Thread(DoWork);
workerThread.Priority = ThreadPriority.Highest;
workerThread.Start(SynchronizationContext.Current);
Related
I have a Winform APP that does a simple task.
There is an event listener for a button that create a new thread:
ThreadStart work = (addToList);
Thread thread = new Thread(work);
thread.Start();
Now I need the second part of the calculation ot be done with the UI thread (so my instruction ask)
"
Note that you need to access the list box within the UI thread (hint: you can use Control.Invoke for WinForm
"
Any idea how to do so?
The Invoke method needs a delegate (reference to a function) and will schedule that on UI thread. Considering that all the elements on the form share the same thread as UI thread, you can use the Invoke method from either the form or any of its controls:
class MyForm
{
private void Button_Click(object sender, EventArgs e)
{
var thread = new Thread(new ThreadStart(FullCalculation));
thread.Start();
}
private void FullCalculation()
{
OffUIThreadCalculation();
this.Invoke(OnUIThreadCalculation); // Schedules OnUIThreadCalculation to run on the UI thread of `this`, aka the form
}
private void OffUIThreadCalculation()
{
// UI elements should not be used here
}
private void OnUIThreadCalculation()
{
// UI elements can be used here
}
}
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";
}
I am trying to create dynamically custom userControl in background thread.
This is my method where I am creating new thread:
var thread = new Thread(CreateItemInBackgroundThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
And this is method CreateItemInBackgroundThread:
var uc = new MyUserControl();
UserControl item = uc;
AllControls.Add(item);
//Here I am trying to add control to a current Tab
foreach (var currentTab in _allTabs)
{
currentTab.DocumentWindow.Dispatcher.BeginInvoke(new Action(() =>
{
if (tab.DocumentWindow.IsSelected)
{
tempTab = tab;
tempControl = item;
finish = true;
}
}));
}
This is my finish property
bool finish
{
get { return _finish; }
set
{
_finish = value;
if (_finish)
{
tempTab.AnimatedCanvas.Dispatcher.BeginInvoke(new Action(() => tempTab.AnimatedCanvas.Children.Add(tempControl)));
}
} // Here I get error - The calling thread cannot access this object because a different thread owns it
}
How can I avoid this error and why this error happend?
as the error says, you can't access the this object because a different thread owns it, so you can invoke that thread using Invoke(Delegate Method)
you can check if invoke required using tempTab.InvokeRequired
This error is coming because u must be doing the different tasks on the same thread like U cannot make a thread go async and update the UI using the same thread.It will cause conflict.because UI thread is the main thread.
You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on.. for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
RunWorkerAsync() will make your thread go async and work on background
this way it will not cause any thread Error too..
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);
In a C#.NET windows application (winforms) I set the visibility of the checkbox to false:
checkBoxLaunch.Visible = true;
I started a thread.
Thread th = new Thread(new ThreadStart(PerformAction));
th.IsBackground = true;
th.Start();
The thread performs some stuff and sets the visibility to true:
private void PerformAction()
{
/*
.
.// some actions.
*/
checkBoxLaunch.Visible = true;
}
After the thread finishes its task, the checkbox is not visible to me.
What am I missing?
You shouldn't make UI changes within a non-UI thread. Use Control.Invoke, Control.BeginInvoke or BackgroundWorker to marshal the call back to the UI thread. For example (assuming C# 3):
private void PerformAction()
{
/*
.
.// some actions.
*/
MethodInvoker action = () => checkBoxLaunch.Visible = true;
checkBoxLaunch.BeginInvoke(action);
}
Search for any of Control.Invoke, Control.BeginInvoke or BackgroundWorker to find hundreds of articles about this.