I've searched around to find the best way to terminate a non UI thread. What I found is that, in short, the thread is in a while loop and you simply set the while loop condition to false to make the thread exit the loop:
https://msdn.microsoft.com/en-us/library/7a2f3ay4(v=vs.80).aspx
Fine but my thread is not in a while loop. The thread is started and then its dispatcher is used. It's actually a thread in a C# dll. The dll itself is using a second C++ dll through a SWIG interface. Long story short, I need a dispatcher.
Now before going further, I would like to mention that everything works fine. But the only way I found to stop the dll thread is by calling Thread.Abort(). I would like to this in a more graceful way.
From the UI thread, that uses the C# dll, an instance of the dll class is created:
public CSomethingDll()
{
// This is the dll class constructor.
using (m_startEvent = new ManualResetEvent(false))
{
m_mainSdkThread = new Thread(DllThreadEntryPoint);
m_mainSdkThread.Name = "DLL THREAD";
m_mainSdkThread.Start(m_startEvent);
m_startEvent.WaitOne();
}
}
[STAThreadAttribute]
public void DllThreadEntryPoint (object o)
{
m_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
(o as ManualResetEvent).Set();
System.Windows.Threading.Dispatcher.Run();
// The dispatcher is now running and ready for calls to BeginInvoke().
}
The dispatcher of the dll thread is now running. The UI thread can now initialize the dll by calling the InitializeA method of the dll:
public void InitializeA()
{
m_dispatcher.BeginInvoke(EvInitialize);
}
Now, when the UI thread is terminating, when the application is closing, the UI thread calls the FinalizeA method:
public void FinalizeA()
{
m_dispatcher.BeginInvoke(EvFinalize);
}
Inside the dll thread, stuff is done to terminate everything and release the resources. Everything works fine. But the only way I found to stop the dll thread is by calling Thread.Abort():
m_dispatcher.Thread.Abort();
Now, is there some trick to stop the dll thread through the Dispatcher? Some way to stop the dispatcher that will then terminate the associated thread?
Thanks!
How do you terminate your UI thread, do you properly exit your dispatcher?
You really need to call Dispatcher.ExitAllFrames so that the Run method exits.
Related
We have a program that can run as a service or as a winforms app. We do different behaviour based on command line parameters passed in.
If we are running as a form, I think we want our entry point to be STAThread.
CA2232: Mark Windows Forms entry points with STAThread
http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&l=EN-US&k=k%28CA2232%29;k%28TargetFrameworkMoniker-.NETFramework
But if we are running as a service do we want our entry point to be an MTAThread? How do people normally deal with this?
We have found some crash dumps (while running as a service ) where we seem to get a stuck finalizer.
We don't get this problem if out main entry point is not marked as an STAThread.
Thread 2:
IP
00:U 00000000779312fa ntdll!NtWaitForSingleObject+0xa
01:U 000007fefd6d10dc KERNELBASE!WaitForSingleObjectEx+0x79
02:U 000007fefdd1e68e ole32!GetToSTA+0x8a
03:U 000007fefde53700 ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0x13b
04:U 000007fefde5265b ole32!CRpcChannelBuffer::SendReceive2+0x11b
05:U 000007fefdd0daaa ole32!CAptRpcChnl::SendReceive+0x52
06:U 000007fefdd1cbe6 ole32!CCtxComChnl::SendReceive+0x15c
07:U 000007fefde5205d ole32!NdrExtpProxySendReceive+0x45
08:U 000007fefdb3b949 rpcrt4!NdrpClientCall3+0x2e2
09:U 000007fefde521d0 ole32!ObjectStublessClient+0x11d
0a:U 000007fefdd0d8a2 ole32!ObjectStubless+0x42
0b:U 000007fefdd2ea07 ole32!CObjectContext::InternalContextCallback+0x31537
0c:U 000007fefdd349d1 ole32!CObjectContext::ContextCallback+0x81
0d:U 000007fef4e439b6 clr!CtxEntry::EnterContext+0x232
0e:U 000007fef4e4383c clr!RCW::EnterContext+0x3d
0f:U 000007fef4e437e6 clr! ?? ::FNODOBFM::string'+0x8c449
10:U 000007fef4e437a9 clr! ?? ::FNODOBFM::string'+0x8b99d
11:U 000007fef4ed326e clr!SyncBlockCache::CleanupSyncBlocks+0xc2
12:U 000007fef4ed319f clr!Thread::DoExtraWorkForFinalizer+0xdc
13:U 000007fef4dfab47 clr!WKS::GCHeap::FinalizerThreadWorker+0x109
14:U 000007fef4d4458c clr!Frame::Pop+0x50
15:U 000007fef4d4451a clr!COMCustomAttribute::PopSecurityContextFrame+0x192
16:U 000007fef4d44491 clr!COMCustomAttribute::PopSecurityContextFrame+0xbd
17:U 000007fef4e21bfe clr!ManagedThreadBase_NoADTransition+0x3f
18:U 000007fef4e21d90 clr!WKS::GCHeap::FinalizerThreadStart+0xb4
19:U 000007fef4da33de clr!Thread::intermediateThreadProc+0x7d
1a:U 00000000777d59ed kernel32!BaseThreadInitThunk+0xd
1b:U 000000007790c541 ntdll!RtlUserThreadStart+0x1d
This is a standard finalizer thread deadlock. Always a bug in your code, pretty easy to make such a mistake in a service or console mode app. It occurred because you use a single-threaded COM object in your code. Very common, the vast majority of COM classes are like the vast majority of .NET classes and not thread-safe at all. With a difference in COM, it takes care of the thread-safety requirement automatically.
And yes, the apartment type of the thread on which you call the methods of the COM server is a very important detail. When you select MTA then you leave it up to COM to keep the object thread-safe. When you select STA then you make a promise that your thread is well-behaved and accommodates the need of such a thread-unsafe object. Such a promise is only easy to implement in a GUI app.
Two basic requirements to fulfill that promise. You must never block the thread, waiting for some kind of synchronization object to get signaled. And you must pump a message loop, Application.Run() in a .NET program. The message loop is required to get a call from a worker thread to run on the thread that owns the COM object, thus keeping it thread-safe. The never-block requirement is there to ensure that such a call can make progress and not deadlock because the STA thread is blocked and not pumping.
Breaking those promises get you in trouble, code will deadlock. Like the finalizer did, it is trying to release the object, making the call to IUnknown.Release() in a thread-safe way. But the thread that owns the COM object is catatonic. Either because it never called Application.Run() or because it is blocked, we can't tell which.
You could make the thread join the MTA, but that isn't always possible and it has pretty grave perf consequences. When you do, the COM runtime is forced to give the object a safe home and will create a thread for it. That's dead easy, but has two basic problems. Every single call to the COM object will get marshaled, that can be very expensive. As much as x10000 slower on a simple property getter call. And the author of the COM component has to help, he needs to provide a proxy and stub implementation. Extra code that is required to copy the arguments of the method call from the caller thread to the owner thread and copy the result back. Very easy to do in .NET, thanks to Reflection, not quite that easy to do in COM. An author often doesn't realize the need and skips that requirement. You'll now be forced to use STA.
It isn't clear from the question which requirement you overlooked. The debugger can show you the other thread that owns the COM server, easy enough to recognize deadlock that way. Or just forgetting to call Application.Run() of course, you see that from your code. You can find code that makes a safe home for such a COM server in this post.
Why not do both. You don't need the entry point to be a STA Thread, you just need the message pump to be a STA thread. All you need to do is start up a non background STA thread when you are running as a form and let the "main thead" go out of scope.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[MTAThread]
static void Main()
{
var args = Environment.GetCommandLineArgs();
if (args.Contains("--service", StringComparer.OrdinalIgnoreCase))
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
else
{
Thread formsThread = new Thread(FormsMain);
formsThread.IsBackground = false;
formsThread.SetApartmentState(ApartmentState.STA);
formsThread.Name = "New Main";
formsThread.Start();
//The program will continue on here and exit Main but the program
// will not shutdown because formsThread is not a background thread.
}
}
static void FormsMain()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
The STAThread atribute has effect before running thread. But method Main() is called after thread start.
To deal with this issue - run other thread:
static class Program
{
static void Main()
{
if (RunningAsWindowsServiceCondition)
{
// run aaplication in windows service mode
}
else
{
// run as WinForms application
var thread = new Thread(RunAsWinForms);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
private static void RunAsWinForms()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
I would simply start a new thread as a non-background-thread and let the mainthread terminiate
using System;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Boolean isService = false; // Figure it out by args, ...
Thread t = new Thread(new ParameterizedThreadStart(HelperMain));
t.IsBackground = false;
t.Priority = ThreadPriority.Normal;
if (isService)
{
t.SetApartmentState(ApartmentState.MTA);
}
else
{
t.SetApartmentState(ApartmentState.STA);
}
t.Start(args);
}
static void HelperMain(Object o)
{
Console.WriteLine(Thread.CurrentThread.GetApartmentState());
Console.ReadLine();
}
}
}
When I started main thread I also started a second thread, but the second thread still waits for the main thread. I expected that when I started a new thread it would go do work without being connected to the main thread. So why does panel1 become visible after the main thread finishing its job?
private void comboBox1_SelectedIndexChanged_1(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(threadUI));
thread.Start();
// This class is loading something from the server on the main thread
excel.get_data_from_excel(comboBox1.SelectedItem.ToString(), this);
}
private void threadUI()
{
if (panel1.InvokeRequired)
{
panel1.Invoke(new newDelegate(threadUI));
}
else
{
panel1.Visible = true;
}
}
The Invoke method will not return until the main thread executes the delegate. If you want the background thread to continue without waiting for the main thread, use BeginInvoke instead.
However, be aware that only one thing can be occurring on the main thread. You can call Invoke or BeginInvoke, but the delegate won't be processed until the main thread is idle. That is, if get_data_from_excel takes a long time, your panel1.Visible=true will not take effect until get_data_from_excel completes, comboBox1_SelectedIndexChanged_1 returns, and the main thread becomes idle.
If you truly want to make these things "parallel", you must execute get_data_from_excel in a background thread.
You're doing long running non-UI work in the UI thread.
The second thread that you create is doing nothing but calling Invoke and doing a bit of work. What Invoke does is run some code in the UI thread, which is currently busy doing some non-UI work. It won't be scheduled to run until after that work finishes.
What you should do is do that long running non-UI work in another thread, rather than the UI thread.
It looks like you're confused about Invoke().
Invoke() is used to queue up a delegate for the thread that displays panel1. However, Invoke() blocks UNTIL that delegate has run to completion. Therefore, you have your second thread blocking at Invoke().
If you would like to have an action run on the main thread, while calling it from the second thread WITHOUT blocking... then use BeginInvoke(). It will queue up the delegate and then return immediately.
Servy's Comment
Servy brings up a good point. Whats the point of the second thread, if it is just going to immediately call the first? There isn't any need to create a second thread if you are just going to immediately adjust a control's property.
But it looks like you are grabbing data from excel. That section of code should be in the second thread... and then with it's output use BeginInvoke().
if i use code just like this it also waiting for the complete next
line finishing its job
private void comboBox1_SelectedIndexChanged_1(object sender, EventArgs e)
{
panel1.Visible = true;
excel.get_data_from_excel(comboBox1.SelectedItem.ToString(), this);
}
I have the following code:
public class GUI
{
public void threadTask()
{
while(MY_GLOBAL_VARIABLE)
{
// do something
}
}
}
// Execute Thread
GUI gui = new GUI();
Thread t = new Thread(threadTask);
t.Start();
This seems like a messy way to do this. Any better approach how to reference it and kill it instantly? Thanks!
UPDATE: who ever gave me a downvote, LEARN TO READ! I clearly specified what I'm trying to 'kill' in the title, tags and code, at least next time read the post before casting a vote.
What you have is fine, the thread will be cleaned up as soon as it's finished processing.
You don't kill/dispose a thread, it has no IDisposable.
You could put the thread start in a method, so you can call it more then once, when it finished the first time.
You can use:
t.Join();
to catch when the thread finished.
The call to Join() is what de-allocates the thread. You don't have to do anything else. Just make sure that the threads clean up any resources they might be using before they exit.
Web Developer here and need some advice on how to achieve what must be a common requirement in Windows Forms.
I have a windows client app that calls a business object in a separate project to perform some long running tasks. Difference to other examples is that the process live in another class library i.e. Business.LongRunningTask();
I have a list box in the client that I would like to have logged to by the task. I can run the process on the UI thread passsing in the instance of the textbox and calling Application.DoEvents() when I log to the textbox from within the task. All fine, but not elegant and would prefer not to call Application.DoEvents();
If I run the long running process on a separate thread using delegates I cannot access the textbox or delegates created in the windows client form which rules out BeginInvoke calls.
Surely this is bad design on my part and would appreciate some feedback.
You're looking for the BackgroundWorker class.
To execute a time-consuming operation in the background, create a BackgroundWorker and listen for events that report the progress of your operation and signal when your operation is finished.
You can find a complete example here: http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.100).aspx#Y1351
I can run the process on the UI thread
passsing in the instance of the
textbox and calling
Application.DoEvents() when I log to
the textbox from within the task.
Yes, you could also pass in an instance of ILoggingINnterface that you have used to put in the code to write to the text box FROM WITHIN THE UI and thus have taken care of all the nice BginInvoke stuff ;)
If I run the long running process on a
separate thread using delegates I
cannot access the textbox or delegates
created in the windows client form
which rules out BeginInvoke calls.
Ah. No. You just most invoke back to the dispatcher thread then you can access all the UI elemente you like.
Yeah, avoid Application.DoEvents().
To marshall the call back onto the UI thread, call this.Invoke(YourDelegate)
To access UI elements from a different thread, you can use control.Invoke to call a delegate on the owning thread.
I used this at one point to create a live log screen which was updated from a timer while a different worker thread was running. Heres a simplified version:
public class DifferentClassLibrary
{
public delegate void StringDataDelegate(string data);
public event StringDataDelegate UpdatedData;
public void DoStuff()
{
if (UpdatedData != null)
{
Thread.Sleep(10000);
UpdatedData("data");
}
}
}
And in the winform:
public void UpdateTextBoxCallback(string data)
{
if (uiTextBoxLiveLogView.InvokeRequired)
{
uiTextBoxLiveLogView.Invoke(new DifferentClassLibrary.StringDataDelegate(UpdateTextBoxCallback), data);
}
else
{
uiTextBoxLiveLogView.Text += data;
}
}
void Main()
{
DifferentClassLibrary test = new DifferentClassLibrary();
test.UpdatedData += UpdateTextBoxCallback;
Thread thread = new Thread(new ThreadStart(test.DoStuff));
thread.Start();
}
I am using a background thread to initialize an instrument over USB. The UI hangs when I try to open the device. I would expect the background thread to pause when calling Open on the device, but not the UI thread. I am testing this with no UI interaction from the background thread. I don't know how to debug the problem, and it's too broad a question, but perhaps someone has seen something like this before. There is nothing wrong with the ActiveX interop as far as I know, the device works correctly. This is the general approach:
using System;
using FancyVoltmeterLibrary;
namespace SOQuestion
{
public class MeterClass
{
private FancyVoltmeter meter;
private Thread meterThread;
public MeterClass()
{
// Create instance of ActiveX/COM object.
meter = new FancyVoltmeter();
meterThread = new Thread(UpdateMeter);
meterThread.Name = "Meter Thread";
meterThread.Priority = ThreadPriority.Normal;
meterThread.IsBackground = true;
meterThread.Start();
}
private void UpdateMeter()
{
while(true)
{
Thread.Sleep(1000);
if(!meter.IsOpen())
{
// Meter may be powered off here.
// The call to Open takes about 1 second.
// UI hangs during the call???
meter.Open();
}
// code to read meter goes here.
}
}
}
}
Edit: Perhaps unclear what I meant. By 'hang' I should say 'freezes momentarily'.
Does meter require running in an STA? Is the call to Open() actually being marshalled back to the UI thread for this reason?
You can verify this is true by looking at the callstack of the hung UI thread in the debugger.
How long time does the instantiation of the FancyVoltmeter take? Could it be that it is not the Open method that causes the UI freeze, but creating the COM object (which is done on the UI thread)?
If that turns out to be the case, moving the creation of this object to happen on the new, separate worker thread should take care of the problem.
Edit: I saw now that you already found this out in your comment to Michael...
I would suggest you wrap the call to meter.open() in a separate method, and call that method from within the updateMeter() method using Invoke() or BeginInvoke() construct on the form or parent control. Doing this will marshal the action back on to the UI thread and should execute gracefully. I hope this helps.
Consider using a BackgroundWorker for this task.