Aborted thread can not be started a second time - c#

I have a problem and don't know how to solve that. I'm starting a new thread:
private void button_Click(object sender, EventArgs e)
{
Thread thrd = new Thread(new ThreadStart(loadingScreenStart));
thrd.Start();
//setting some variables, entering some methods etc...
thrd.Abort();
}
public void loadingScreenStart()
{
splashScreen splashObj = splashScreen.GetInstance();
Application.Run(splashScreen.GetInstance());
}
In another form I have:
private static splashScreen m_instance = null;
private static object m_instanceLock = new object();
public static splashScreen GetInstance()
{
lock (m_instanceLock)
{
if (m_instance == null)
{
m_instance = new splashScreen();
}
}
return m_instance;
}
That works fine but when I hit the button a second time then I get an exception that there is no access to the discarded object. Why and how to solve that? I mean after the thread gets aborted I create a new one when hitting the button again.

It's not the thread that is discarded, but the splashScreen instance. You should just create a new one, not try to reuse the old one.

Thread thrd = new Thread(new ThreadStart(loadingScreenStart));
thrd.Start();
//setting some variables, entering some methods etc...
thrd.Abort();
Why do you call thrd.Abort()? Did u know thread finish yet?
You must wait thread finished.
And use double checking here
public static splashScreen GetInstance()
{
if (m_instance == null)
{
lock (m_instanceLock)
{
if (m_instance == null)
{
m_instance = new splashScreen();
}
}
}
return m_instance;
}
And maybe when you call Run splashscreen, it was disposed. Try create and capture it to a field and pass it to your method. Try this.
Task.Run(() =>
var splashObj = splashScreen.GetInstance();
Application.Run(splashObj);
}));

Ok I solved it somehow. Don't think that this is a good solution but it works. Instead of aborting the thread I just hide the Form of the SplashScreen and check if the thread is already running. If yes then I just Show the Form. If not I create a new instance.

This is overly complicated and unsafe. If you really want to handle this using a "splash screen", why not try something like this?
using (var splashScreenForm = SplashScreen.ShowSplashScreen())
{
// Do your work
}
Where SplashScreen has methods like this:
public static SplashScreen ShowSplashScreen()
{
var form = new SplashScreen();
new Thread(() => Application.Run(form)).Start();
return form;
}
public override void Dispose(bool disposing)
{
if (disposing)
{
if (InvokeRequired)
{
Invoke(() => base.Dispose(true));
return;
}
base.Dispose(true);
}
else base.Dispose(disposing);
}

After taking another look at the whole thing I realized there's another problem here, and this is the entirely wrong direction to approach it from.
In your button_Click event you're obviously doing a lot of complicated stuff that takes a lot of time. Otherwise you wouldn't need to show a splash screen. However doing this in an event handler is a bad idea in itself. You totally lock up the UI thread and Windows will soon consider that window to be "hanged". It cannot even repaint itself!
So you should approach this from the completely opposite direction. Instead of trying to show the splash screen from another thread while using the UI thread for heavy work, just move the heavy work to the other thread! Then the splash screen won't need anything exotic like calling Application.Start() in another thread. Just a simple .ShowModal() will be enough. And when the other thread is done with its work, it can just call .Close() on your splash screen.
In a well designed application there is almost never a need for a second UI thread. It's the heavy work that needs to be moved to other threads, not UI.
Just be aware that if you want to manipulate the UI from another thread, you'll need to do some Invoke() stuff. Read more here: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.110).aspx

Related

Deadlock when updating a UI control from a worker thread

To simplify the explanation of the strange behavior I am experiencing, I have this simple class named Log which fires 1 log events every 1000msec.
public static class Log
{
public delegate void LogDel(string msg);
public static event LogDel logEvent;
public static void StartMessageGeneration ()
{
for (int i = 0; i < 1000; i++)
{
logEvent.Invoke(i.ToString());
Task.Delay(1000);
}
}
}
I have the Form class below which is subscribed to the log events of the Log class so it can handle them and display in a simple text box.
Once a log message arrives, it is added to a list. Every 500msec, a timer object access that list so its content can be displayed in a text box.
public partial class Form1 : Form
{
private SynchronizationContext context;
private System.Threading.Timer guiTimer = null;
private readonly object syncLock = new object();
private List<string> listOfMessages = new List<string>();
public Form1()
{
InitializeComponent();
context = SynchronizationContext.Current;
guiTimer = new System.Threading.Timer(TimerProcessor, this, 0, 500);
Log.logEvent += Log_logEvent;
}
private void Log_logEvent(string msg)
{
lock (syncLock)
listOfMessages.Add(msg);
}
private void TimerProcessor(object obj)
{
Form1 myForm = obj as Form1;
lock (myForm.syncLock)
{
if (myForm.listOfMessages.Count == 0)
return;
myForm.context.Send(new SendOrPostCallback(delegate
{
foreach (string item in myForm.listOfMessages)
myForm.textBox1.AppendText(item + "\n");
}), null);
listOfMessages.Clear();
}
}
private void button1_Click(object sender, EventArgs e)
{
Log.StartMessageGeneration();
}
}
The problem I see is that sometimes, there is a dead lock (application stuck). Seems that the 2 locks (1st one for adding to the list and the 2nd one for "retrieving" from the list) are somehow blocking each others.
Hints:
1) reducing the rate of sending the messages from 1 sec to 200msec seems to help (not sure why)
2) Somehow something happens when returning to the GUI thread (using the synchronization context) and accessing the GUI control. If I don't return to the GUI thread, the 2 locks are working fine together...
Thanks everyone!
There's a few problems with your code, and a few... silly things.
First, your Log.StartMessageGeneration doesn't actually produce a log message every second, because you're not awaiting the task returned by Task.Delay - you're basically just creating a thousand timers very quickly (and pointlessly). The log generation is limited only by the Invoke. Using Thread.Sleep is a blocking alternative to Task.Delay if you don't want to use Tasks, await etc. Of course, therein lies your biggest problem - StartMessageGeneration is not asynchronous with respect to the UI thread!
Second, there's little point in using System.Threading.Timer on your form. Instead, just use the windows forms timer - it's entirely on the UI thread so there's no need for marshalling your code back to the UI thread. Since your TimerProcessor doesn't do any CPU work and it only blocks for a very short time, it's the more straight-forward solution.
If you decide to keep using System.Threading.Timer anyway, there's no point in manually dealing with synchronization contexts - just use BeginInvoke on the form; the same way, there's no point in passing the form as an argument to the method, since the method isn't static. this is your form. You can actually see this is the case since you omitted myForm in listOfMessages.Clear() - the two instances are the same, myForm is superfluous.
A simple pause in the debugger will easily tell you where the program is hung - learn to use the debugger well, and it will save you a lot of time. But let's just look at this logically. StartMessageGeneration runs on the UI thread, while System.Threading.Timer uses a thread-pool thread. When the timer locks syncLock, StartMessageGeneration can't enter the same lock, of course - that's fine. But then you Send to the UI thread, and... the UI thread can't do anything, since it's blocked by StartMessageGeneration, which never gives the UI an opportunity to do anything. And StartMessageGeneration can't proceed, because it's waiting on the lock. The only case where this "works" is when StartMessageGeneration runs fast enough to complete before your timer fires (thus freeing the UI thread to do its work) - which is very much possible due to your incorrect use of Task.Delay.
Now let's look on your "hints" with all we know. 1) is simply your bias in measurements. Since you never wait on the Task.Delay in any way, changing the interval does absolutely nothing (with a tiny change in case the delay is zero). 2) of course - that's where your deadlock is. Two pieces of code that depend on a shared resource, while they both require to take posession of another resource. It's a very typical case of a deadlock. Thread 1 is waiting for A to release B, and thread 2 is waiting for B to release A (in this case, A being syncLock and B being the UI thread). When you remove the Send (or replace it with Post), thread 1 no longer has to wait on B, and the deadlock disappears.
There's other things that make writing code like this simpler. There's little point in declaring your own delegate when you can just use Action<string>, for example; using await helps quite a bit when dealing with mixed UI/non-UI code, as well as managing any kind of asynchronous code. You don't need to use event where a simple function will suffice - you can just pass that delegate to a function that needs it if that makes sense, and it may make perfect sense not to allow multiple event handlers to be called. If you decide to keep with the event, at least make sure it conforms to the EventHandler delegate.
To show how your code can be rewritten to be a bit more up-to-date and actually work:
void Main()
{
Application.Run(new LogForm());
}
public static class Log
{
public static async Task GenerateMessagesAsync(Action<string> logEvent,
CancellationToken cancel)
{
for (int i = 0; i < 1000; i++)
{
cancel.ThrowIfCancellationRequested();
logEvent(i.ToString());
await Task.Delay(1000, cancel);
}
}
}
public partial class LogForm : Form
{
private readonly List<string> messages;
private readonly Button btnStart;
private readonly Button btnStop;
private readonly TextBox tbxLog;
private readonly System.Windows.Forms.Timer timer;
public LogForm()
{
messages = new List<string>();
btnStart = new Button { Text = "Start" };
btnStart.Click += btnStart_Click;
Controls.Add(btnStart);
btnStop =
new Button { Text = "Stop", Location = new Point(80, 0), Enabled = false };
Controls.Add(btnStop);
tbxLog = new TextBox { Height = 200, Multiline = true, Dock = DockStyle.Bottom };
Controls.Add(tbxLog);
timer = new System.Windows.Forms.Timer { Interval = 500 };
timer.Tick += TimerProcessor;
timer.Start();
}
private void TimerProcessor(object sender, EventArgs e)
{
foreach (var message in messages)
{
tbxLog.AppendText(message + Environment.NewLine);
}
messages.Clear();
}
private async void btnStart_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
var cts = new CancellationTokenSource();
EventHandler stopAction = (_, __) => cts.Cancel();
btnStop.Click += stopAction;
btnStop.Enabled = true;
try
{
await Log.GenerateMessagesAsync(message => messages.Add(message), cts.Token);
}
catch (TaskCanceledException)
{
messages.Add("Cancelled.");
}
finally
{
btnStart.Enabled = true;
btnStop.Click -= stopAction;
btnStop.Enabled = false;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
timer.Dispose();
btnStart.Dispose();
btnStop.Dispose();
tbxLog.Dispose();
}
base.Dispose(disposing);
}
}
SynchronizationContext.Send is run synchronously. When you call it, you actually block the UI thread until the operation is complete. But if UI thread is already in lock state, then it just make sense that you are in deadlock.
You can use SynchronizationContext.Post to avoid this.
I just answer on your question, but the truth is that your code need a "little" refactoring..

Form design messed up when using Show()

I'm trying to make a loading screen window. I use Show() instead of ShowDialog() because I have some code to execute after showing it. When using ShowDialog() form is fine but when using Show() form is messed up. What is causing this and what is the solution? Here is how I did it:
bool closeLoadingWindow = false;
void ShowLoadingWindow()
{
LoadingWindow loadingWindow = new LoadingWindow();
loadingWindow.Show();
while (!closeLoadingWindow);
loadingWindow.Close();
return;
}
public MainWindow()
{
Thread loadingWindowThread = new Thread(ShowLoadingWindow);
loadingWindowThread.Start();
InitializeComponent();
// ...
closeLoadingWindow = true;
}
When using ShowDialog():
When using Show():
The reason ShowDialog is working is because your while loop won't be executing, once the runtime hits that line of code it will stop processing until the form is dimissed.
Your code doesn't make sense, the point of using a thread here is to keep the "busy" code (your while loop) out of the main UI thread so it doesn't block. However, you are trying to create/show your form on the same thread, and a non-UI thread at that.
You don't necessarily need to use Show here, you can use ShowDialog but it is a little bit trickier in terms of dimissing the form etc. However, to solve the problem you have at the minute I would recommend you do:
LoadingWindow _loadingWindow;
void ShowLoadingWindow()
{
if (_loadingWindow == null)
_loadingWindow = new LoadingWindow();
_loadingWindow.Show();
}
void HideLoadingWindow()
{
if (_loadingWindow != null)
{
_loadingWindow.Close();
_loadingWindow.Dispose();
}
}
void LoadSomething()
{
while (...)
{
// busy code goes here
}
// after code is finished, close the form
MethodInvoker closeForm = delegate { HideLoadingWindow(); };
_loadingWindow.Invoke(closeForm);
}
public MainWindow()
{
ShowLoadingWindow();
new Thread(LoadSomething).Start();
}
}
FYI - Depending on the nature of exactly what your trying to do in the thread it might be a better approach to use the Task Parallel Library rather than creating a dedicated thread, various benefits like continuation / cancellation support.

c# forms blocking other control when show dialog called

When my application is loading. I display a progress bar using the code below. The problem is if someone clicks on the toolbar context menu (the way to exit) it will be blocked until this the progress bar is closed. Does anyone know a better way of achieving this?
The reason I'm using ShowDialog is that when I used Show the progress bar wouldn't animate - I'm using the MarqueeStyle.
Thanks
public partial class PopUpProgessBar : Form
{
public PopUpProgessBar()
{
InitializeComponent();
}
Thread t;
private void StartAnmiation()
{
this.Update();
this.ShowDialog();
}
public void Stop()
{
if (t != null)
{
t.Abort();
t.Join();
}
}
public void Start()
{
if (t == null)
{
t = new Thread(new ThreadStart(this.StartAnmiation));
t.Start();
}
}
This code doesn't look quite right. Are you sure it doesn't throw cross-thread violations? In general, your whole metaphor here is wrong. You need to keep the GUI on the GUI thread. Load your application on the background thread and have it send progress updates to the GUI thread.
Your PopupProgressBar form shouldn't be responsible for loading itself in a new thread, that should be done in presumably your main window.
I would get rid of all the thread stuff in PopupProgressBar and make it simply start updating it's marquee. Then, in your main window (OnLoad) you tell it to do it's thing:
bool done = false;
PopupProgressBar splashForm = null;
ThreadPool.QueueUserWorkItem((x) =>
{
using (splashForm = new PopupProgressBar ())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
// do all your initialization work here
// also, during each step of your initialization you could send call a function
// in splashForm to update
done = true;

NullReferenceException when closing a threaded form

Context :
I created an Windows Form application which runs a splash screen before starting. Quickly, here is what contains my Program.cs :
public static Thread splashScreenThread = null;
public static FormSplashScreen formSplashScreen;
[STAThread]
static void Main(string[] args) {
// Show splash screen
splashScreenThread = new Thread(new ThreadStart(ShowSplashScreen));
splashScreenThread.IsBackground = true;
splashScreenThread.Start();
// Load some components in background
LoadComponentsInBackground()
// Hide the splash screen
if (splashScreenThread != null) {
formSplashScreen.Invoke(new MethodInvoker(delegate {
formSplashScreen.Close();
formSplashScreen.Dispose();
}));
splashScreenThread = null;
}
// Start now the application
Application.Run();
}
private static void ShowSplashScreen() {
formSplashScreen = new FormSplashScreen();
formSplashScreen.ShowDialog();
}
Problem :
My problem does not happen everytime I start the application, it seems to be random and to occur more often on some PCs and less on others... So I'm a bit confused, besides I don't really understand where it comes from:
A NullReferenceException is raised on the formSplashScreen.Invoke(...Close...) line, but formSplashScreen is correctly initialized (I checked it while debugging).
I'm not sure if this comes from the thread or from another point...
Possible solution :
I could maybe surround the line causing problem with something like below, but it would only be getting round the problem and I'd rather like understanding it and properly solving it.
while (splashScreenThread != null) {
try {
formSplashScreen.Invoke(new MethodInvoker(delegate {
formSplashScreen.Close();
formSplashScreen.Dispose();
}));
splashScreenThread = null;
} catch (Exception e) {
}
}
This is a race condition that happens because the thread has been created and staterted but the SplashScreen has not been created yet.
In other words you're trying to close the splash screen before it was even created.
You can use EventWaitHandle to make sure that the SplashScreen has been created or at least wait until it's not null.
Another option is to signal the screen it should close and let him handle the closing logic.

How to abort a thread started inside another function?

Monitor moni = new Monitor();
Thread t = new Thread(() => moni.CurrUsage(nics,200));
t.Start();
I start a thread named 't' inside the 'Form1_Load' function. I have added a button. When click on that button the thread 't' should stop executing and create a new thread with these parameters.
Monitor moni = new Monitor();
Thread t = new Thread(() => moni.CurrUsage(nics,950));
t.Start();
I know in the form_load event i can use the
t.Abort();
By making t a member of the form, you can reference it later on in the button-click event handler.
Graceful Abort.
Although t.Abort() gets the job done, you might be left with half-processed data in the thread t. You can catch the ThreadAbortException in thread t to gracefully end processing.
Beware of overlap.
The second problem is that your thread might not have aborted yet while your new thread has started already. You can prevent that by calling t.Join() after calling t.Abort().
Hope this helps.
Make Thread t a private member of your form.
public partial class MainForm : Form
{
private Thread t;
}
One way is to make Thread t a global variable (place outside of Form_Load). Then it can be accessed and modified from any method in that class.
To instantiate the thread, use t = new Thread(.....
Before aborting the thread, make sure it is not null.
You need to make the Thread object accessable in both places that you need to access it.
In this case, making it a private varaible would work.
e.g.
public class MyClass
{
private Thread MyThread
{
get;
set;
}
private void myfunc1()
{
MyThread = new Thread(() => moni.CurrUsage(nics,200));
MyThread.Start();
}
private void myfunc2()
{
MyThread.Abort();
// I really need to wait until this thread has stopped...
MyThread.Join();
}
}
Adding to the already given answers:
Note that .Join() will block your current (UI) thread, leaving your application unresponsive to the user.
Just as another take: avoid using .Abort() by using a flag in your Monitor class to exit the task you are doing if possible. You can then still wait for .Join(), but you have full control of the state in the background thread.
public class Monitor
{
private bool _cancel = false;
public void Cancel()
{
_cancel = true;
}
public void CurrUsage(Nics nics, int n)
{
_cancel = false;
// ...
while (!_cancel)
{
// do some stuff
}
}
}
in your Form
private Monitor _monitor { get; set; }
private Thread _t;
public void Button_Click(...)
{
_monitor.Cancel()
_t.Join() // will return as your background thread has finished cleanly
_t = new Thread(() => _monitor.CurrUsage(nics,950));
t.Start();
}
As others have pointed out, all you need in order to call Abort is a reference to the thread (just like any other object in .NET).
However
You should seriously consider rethinking this approach. In general, calling Abort is discouraged, as it does not give the target thread sufficient opportunity to reach a stopping point. While it's sometimes appropriate (or the only option), it's almost always a better idea to ask the target thread to stop (usually through a volatile bool rather than forcing it like this.
For example,
public class ThreadClass
{
private volatile bool stopRequested;
private Thread thread;
public void Start()
{
stopRequested = false;
thread = new Thread(ThreadMethod);
thread.Start();
}
public void Stop()
{
stopRequested = true;
if(!thread.Join(5000)) thread.Abort(); // forcefully abort if not
// completed within 5 seconds
}
private void ThreadMethod()
{
}
}
Your code then goes into ThreadMethod. Within the method, periodically check the value of stopRequested. If it's true, perform whatever cleanup is necessary (if any) and gracefully return out of the thread. If the content is a loop, the general practice is to place the check at the start of the loop (assuming that the loop is sufficiently tight) and exit early if the value is true. The exact placement is really dependent upon the code, but the general idea is that it should be checked often enough to make the thread exit fairly quickly after it gets set, regardless of when that happens.

Categories

Resources