Accesing two threads in one button click function - c#

This is not throwing me any error but after executing first thread it is not executing second thread. Am I doing anything wrong?
Below is my code:
My button click Function:
private void ImportBothButtonclick(object sender, EventArgs e)
{
// Get the currently selected manufacturer from the combo box
var selected = comboBox.SelectedItem;
// Do we have one?
if (selected != null)
{
// Extract the combo record
var val= (ComboBoxItem)selected;
// Do we have one?
if (val != null)
{
// yes
// Make this on a seperate thread so that the UI continues to work
Invoke(new System.Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
var thread = new Thread(DoFunction1);
thread.Start(val);
}));
Invoke(new System.Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3Enabled = false;
var thread = new Thread(DoFunction2);
thread.Start(val);
}));
}
}
}

Those Actions will not do a thing. The actions are Invoked on the same thread you're currently on.
The threads are currently running parallel. If you want those threads to run in serial but not on the gui thread, you could do something like this:
This is the NON-TASK version.
// not allowed on a non-gui thread.
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
new Thread(() =>
{
DoFunction1();
DoFunction2();
// execute this on the gui thread. (winforms)
this.Invoke(new Action( delegate
{
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = true;
}));
}).Start();
If you want to run them parallel, but wait until they are done:
// not allowed on a non-gui thread.
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
new Thread(() =>
{
ManualResetEvent wait1 = new ManualResetEvent(false);
ManualResetEvent wait2 = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((state) =>
{
DoFunction1();
wait1.Set();
});
ThreadPool.QueueUserWorkItem((state) =>
{
DoFunction2();
wait2.Set();
});
ManualResetEvent.WaitAll(new WaitHandle[] { wait1, wait2 });
this.Invoke(new Action( delegate
{
// execute this on the gui thread. (winforms)
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = true;
}));
}).Start();
But this can be more easy using tasks. Task Parallelism (Task Parallel Library) http://msdn.microsoft.com/en-us/library/dd537609.aspx

Please clarify what is the exact problem you observe?
According to what you have currently stated, the problem lies in "the second thread is not run after the first thread".
So, let me answer to this.
Your code is almost OK. You have overlooked one important thing: your code "new thread / thread.start()" actually do start a new thread, and then it does not wait for that thread to execute nor complete.
lines:
new thread(f1).Start()
new thread(f2).Start()
will not "run F1 on thread1, then run F2 on thread2". Instead, they will "begin to run F1 on thread1 and immediately begin to run F2 on thread2".
To have the F2 executed only after F1 has fully finished, you must somehow 'chain' the two together:
you can create simple "agenda" method and run it instead:
private void doAllTasks()
{
f1();
f2();
}
new thread(doAllTasks).Start()
you can try "chaining" them on the fly via lambdas, which effectively is the same as above:
new thread(() => { f1(); f2(); ).Start()
you can actually run them both immediatelly, but have the F2 join [wait] until the F1's thread ends
var th1 = new thread(f1);
var th2 = new thread(() => {th1.Join(); f2();} )
th1.Start();
th2.Start();
// note that this code is NOT perfect, there's some error handling to do here, etc..
or, you can try some nice and pretty wrappers for all of that like the TPL framework, as seen in Sheridan's answer.
And of course you must be careful with touching UI elements from inside what those other new thread's are running. Sheridan's answer already covers it via TPL ways. Manually, you'd have to use Invoke/BeginInvoke to bounce the UI-related code back to UI thread. In your current code you already have it, but at that place, it is not necessary, since the _Click handler method obviously already runs in UI thread.
Therefore, your current example can be reduced to i.e.:
private void ImportBothButtonclick(object sender, EventArgs e)
{
var selected = comboBox.SelectedItem;
if (selected != null)
{
var val= (ComboBoxItem)selected;
if (val != null)
{
// no Invoke needed, "button_click" handlers
// are already on UI thread, so touching UI things is OK
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
// starting a new thread also does not need Invoke
var thread = new Thread(DoAllFunctions);
thread.Start(val);
}
}
}
private void DoAllFunctions(object something)
{
DoFunction1(something);
DoFunction2(something);
// button1.Enabled = true; - cannot do it here because they are UI
// button2.Enabled = true; - and DoAll is run from other thread.
// button3.Enabled = true; - you must bounce that back to UI thread.
LetTheUIKnowJobsAreFinished(); // <- performed here
}
private void LetTheUIKnowJobsAreFinished()
{
Invoke(new Action(()=>{
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = true;
});
}
Also, as a final note, please go and look at the BackgroundWorker from System.ComponentModel. It has very nice set of events/callbacks that will make all the thread-crossing very easy.
(btw. let me say that again: this code is NOT usable. it is only a sketch. you will find typos, missing colons, missing try-catch-whatever etc!)

Maybe you could do something like this?:
Task.Factory.StartNew(new Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = false;
}), new CancellationToken(), TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith(new Action(() => DoFunction1)).
ContinueWith(new Action(() =>
{
button1.Enabled = false;
button2.Enabled = false;
button3Enabled = false;
}), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith(new Action(() => DoFunction2));

Related

Enable/Disable buttons while thread is working

I have a button that starts two threads
private void CrawdBtn_Click(object sender, EventArgs e)
{
CrawdBtn.Enabled = false;
t = new Thread(AddLinksToList);
b = new Thread(EnqueueFromList);
t.Start();
b.Start();
}
and there are another buttons to pause, Resume, Stop those threads
My question is how can I disable (pause, Resume, Stop) buttons while the threads are working and re enable Crawl after the threads finished
Here is how you could start a Thread and have a way to await its completion:
public static Thread CreateAwaitableThread(Action action, out Task threadCompletion)
{
var tcs = new TaskCompletionSource<bool>();
threadCompletion = tcs.Task;
return new Thread(() =>
{
try
{
action();
}
finally
{
tcs.SetResult(true);
}
});
}
This method returns the newly created Thread, and also a Task that will be completed when the Thread is completed. You could use it like this:
private async void CrawdBtn_Click(object sender, EventArgs e)
{
CrawdBtn.Enabled = false;
Thread t1 = CreateAwaitableThread(AddLinksToList, out var t1c);
Thread t2 = CreateAwaitableThread(EnqueueFromList, out var t2c);
t1.Start();
t2.Start();
await Task.WhenAll(t1c, t2c);
CrawdBtn.Enabled = true;
}
In case of an exception the error will not be propagated through the Task. It is assumed that the delegates already include error handling logic. If not, an unhandled exception will occur as usual.
To solve your problem you can make a thread to check the ThreadState of thread t and thread b
private void btnstart_Click(object sender, EventArgs e)
{
t = new Thread(AddLinksToList);
b = new Thread(EnqueueFromList);
t.Start();
b.Start();
if (threadchecker == null)//this if determines Whether it's the first time or not
{
threadchecker = new Thread(() => ChekingStateOfThreads());
threadchecker.IsBackground = true;
threadchecker.Start();
}
}
Since the thread wants to check the ThreadState of those threads it should always run.
and This is ChekingStateOfThreads
private void ChekingStateOfThreads()
{
while (true)
{
Thread.Sleep(1000);
if (t.ThreadState == ThreadState.Stopped)
{
this.Invoke(new Action(() =>
{
btnpause.Enabled = btnstart.Enabled = false;
btnresume.Enabled = btnstop.Enabled = true;
}));
}
else if (t.ThreadState == ThreadState.Running)
{
this.Invoke(new Action(() =>
{
btnstart.Enabled = btnresume.Enabled = false;
btnpause.Enabled = btnstop.Enabled = true;
}));
}
else if (t.ThreadState == ThreadState.Aborted)
{
this.Invoke(new Action(() =>
{
btnstart.Enabled = true;
btnpause.Enabled = btnresume.Enabled = btnstop.Enabled = false;
}));
}
else if (t.ThreadState == ThreadState.Suspended)
{
this.Invoke(new Action(() =>
{
btnpause.Enabled = btnstart.Enabled = false;
btnresume.Enabled = btnstop.Enabled = true;
}));
}
}
}
The concept of function is pretty simple. Each 1 second the thread is check the state of thread t.
See Why use Invoke on Controls in .net? to figure out why should we use INVOKE.
To abort the threadchecker just use the Form_closing Event
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
threadchecker.Abort();
}

Showing busy indicator on a STA thread

I have a long operation wehre I'd like to show the Extended Toolkits busy indicator. I made a previous post about this and it was fixed Wpf Extended toolkit BusyIndicator not showing during operation. However, during that call I have to interact with a UI element (canvas) and I get a "The calling thread must be STA, because many UI components require this". I understand (now) that a background worker(see code):
private void CboItemId_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
BackgroundWorker _backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
ItemSearchBusyIndicator.IsBusy = true;
// Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
if (RdoItemSearch.IsChecked == false) return;
///backgroundWorker_DoWork(null, null);
if (CboItemId.SelectedValue == null) return;
if (CboItemId.SelectedValue.ToString() != string.Empty)
{
selectedItem = CboItemId.SelectedValue.ToString();
_backgroundWorker.RunWorkerAsync();
}
// Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow;
}
public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
LoadItemData(selectedItem);
}
uses MTA and cannot be set to STA. So i tried calling the internal function that uses the UI elelment in its own thread:
public void LoadItemData(string itemId)
{
Axapta ax = new Axapta();
files.Clear();
try
{
ax.Logon(Settings.Default.Server, null, Settings.Default.Test, null);
AxaptaContainer path = (AxaptaContainer)ax.CallStaticClassMethod(Settings.Default.ClassName, Settings.Default.ItemData, itemId);
for (int i = 1; i <= path.Count; i++)
{
AxaptaContainer somestring = (AxaptaContainer)path.get_Item(i);
for (int j = 1; j <= somestring.Count; j += 2)
{
string extension = Path.GetExtension(somestring.get_Item(j + 1).ToString().ToLower());
if (extension == ".jpg"
|| extension == ".jpeg"
|| extension == ".gif"
|| extension == ".png"
|| extension == ".bmp"
|| extension == ".pdf")
/* key=path - value=description */
files.Add(somestring.get_Item(j + 1).ToString(), somestring.get_Item(j).ToString());
}
}
// _canvas.Children.Clear();
Thread t = new Thread(new ThreadStart(LoadPictures));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
ax.Logoff();
}
}
Heres where I interact with the canvas element:
private void LoadPictures()
{
foreach (DictionaryEntry filePath in files)
{
try
{
Picture p = new Picture();
ToolTip t = new ToolTip();
t.Content = filePath.Value;
p.ToolTip = t;
TextBlock tb = new TextBlock();
tb.Text = filePath.Value.ToString();
Canvas.SetTop(tb, y);
Canvas.SetLeft(tb, x);
p.ImagePath = filePath.Key.ToString();
p.OriginalImagePath = filePath.Key.ToString();
p.ImageName = filePath.Value.ToString();
_canvas.Children.Add(p); //<-------This is where i seem to error
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.Message,"File Load Error",MessageBoxButton.OK,MessageBoxImage.Error);
}
}
}
but I get a "The calling thread cannot access this object because a different thread owns it"
I don't know how to call the long running (LoadItemData()) function while showing the BusyIndicator without a backgroundworker. Any help appreciated
There are multiple approaches:
1) Async binding, it's not recommended, but it is there. You can run long running task in property getter, framework will prevent UI from blocking, when it is finished - UI will get updated.
2) Use BackgroundWorker or Task/Thread to run code, but invoke it into UI thread. In your example:
Dispatcher.InvokeAsync(() => _canvas.Children.Add(p));
3) You can block UI thread of main window completely, no problems. But to indicate about its being busy you can create window in another thread and show there busy status (run animations, etc):
var thread = new Thread(() =>
{
var window = new SomeWindow();
window.ShowDialog();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();

How to show Progressbar untill a process ends

I'm having a problem when I' trying to show a progress bar until a external process ends which I started for the WPF window.
The code is like this:
private void Button1_Click(object sender, RoutedEventArgs e)
{
Button1.IsEnabled = false;
Button1.Content = "Please Wait";
ProgressBar1.Visibility = Visibility.Visible;
if (a == 1 && b == 0)
{
var processStartInfo = new ProcessStartInfo(#"External Process Path 1");
processStartInfo.Verb = "runas";
try
{
Process.Start(processStartInfo);
}
catch (Win32Exception ex)
{
MessageBox.Show(ex.ToString(), "Run As",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
if (b == 1 && a == 0)
{
var processStartInfo = new ProcessStartInfo(#"External Process Patch 2");
processStartInfo.Verb = "runas";
try
{
Process.Start(processStartInfo);
}
catch (Win32Exception ex)
{
MessageBox.Show(ex.ToString(), "Run As",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
Button2.IsEnabled = true;
ProgressBar1.Visibility = Visibility.Hidden; //This is what I want to toggle after process ends
}
I've tried Thread.Sleep(time) method and also for-loop but nothing seems to work.
I'm very new in WPF. So, please try to be a little brief.
Thanks,
D.K.
do you know how long the external process lasts? if you don't you can try to set the IsIndeterminate property to true on your progress bar. this will show a continuous animation. When your process returns you can set it to false again in order to stop the animation.
also, in your code, i think you're not waiting for the process to finish. you could do this by using the code below.
Process p = Process.Start("IExplore");
p.WaitForExit();
please note that WaitForExit() blocks the current thread. as a result the app will stop responding. to keep the UI responsive you might like to start your process on a different thread like below.
private void onClick_Handler(object sender, EventArgs e) {
//disable button here
Task.Factory.StartNew(() => {
Process p = Process.Start("IExplore");
p.WaitForExit();
//enable button here. make sure to do this on the UI thread
//since you're doing this in the code-behind you should have access
//to the dispatcher
Dispatcher.BeginInvoke((Action)OnUpdateUI);
});
}
private void OnUpdateUI(){
}
In the above code, you are starting a process but not waiting for it to end, so the debugger when executes your:
Process.Star("Add Some Task");
, it jumps on to the next statement which is
button2.IsEnabled = true;
and so on. As a result your ProgressBar1 is not visible to you.
Please wait for the Process to end first. Write
Process.WaitForExit();
just after your statement
Process.Start("Your already started task");
Also you can create an Asynchronous thread to run in parallel.
Example:
Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
taskA.Start();
taskA.Wait();
Also in your above code, you are only displaying ProgressBar but not updating its value with time. So as a result ProgressBar will be visible only with initial value.
For ProgressBar, do something like,
ProgressBar ProgressBar1 = new ProgressBar();
ProgressBar1.Maximum = 100;
ProgressBar1.Minimum = 0;
Task.Start();
if( Task.Status == "Running")
{
ProgressBar1.Value = 50;
}
if( Task.Status == "completed")
{
ProgressBar1.Value =100;
}
else
{
ProgressBar.Value=0;
Task.Wait();
}
The code above mentioned may not be syntactically right. So do look for correct syntax.

Updating a Label with the backgroundworker progress

I am reading line by line console of an external exe with the help of a backgroundworker, i am assigning each line of console to a label. the problem is the label is not updating with the console line. code is given below
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
int i = 0;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.FileName = EXELOCATION;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = Program.path;
startInfo.RedirectStandardOutput = true;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (exeProcess = Process.Start(startInfo))
{
using (StreamReader reader = exeProcess.StandardOutput)
{
string result;
while ((result = reader.ReadLine()) != null)
{
// object param = result;
e.Result = result;
bgWorker.ReportProgress(i++);
}
}
}
}
catch
{
// Log error.
}
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label.Text = e.ToString();
label.Refresh();
}
how can i solve this issue
try this:
label2.Invoke(new Action(() => { label2.Text = e.ToString(); }));
label2.Invoke(new Action(() => { label2.Refresh(); }));
That code probably doesn't work because you're trying to update an UI element from a non-UI thread (aka background thread).
If you're using WPF, you should use the Dispatcher to request that the label be changed in the UI thread. If you're using another framework, try that framework's equivalent class.
In your ProgressChanged method, try this instead:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
() => {
label.Text = e.ToString();
});
If this is in another thread (and you are in a winforms application), you might need to use the Control.InvokeRequired
public void UpdateProgress (int progress)
{
if (label.InvokeRequired)
{
this.Invoke(()=> UpdateProgress(progress));
}
else
{
label.Text = progress.ToString();
}
}
This method checks whether it's run on the UI thread, and if it is not, it calls itself on the UI thread. If it is already on the UI thread, it simply updates the label.

WPF application crashes when using System.Threading.Thread

I have a button named submit_Button which has the following code in the OnClicked event:
Thread thread = new Thread(new ThreadStart(heavyWork));
thread.Start();
heavyWork function code :
private void heavyWork()
{
DisableUI();
string Name = Name_textBox.Text;
celebrityName = Name.Replace(" ", "+");
string queryURL = "http://stackoverflow.com";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(queryURL);
request.Method = "GET";
// make request for web page
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader htmlSource = new StreamReader(response.GetResponseStream());
string htmlStringSource = string.Empty;
htmlStringSource = htmlSource.ReadToEnd();
response.Close();
//var regex = new Regex(#"<FONT class=""result"">(.*?)</FONT>");
var regex = new Regex(#"<span class=""kno-a-v"">(.*?)</span>");
var match = regex.Match(htmlStringSource);
var result = match.Groups[1].Value;
result = HttpUtility.HtmlDecode(result);
MessageBox.Show(result);
EnableUI();
}
// Functions
private void DisableUI()
{
celebrityName_textBox.IsEnabled = false;
submit_Button.IsEnabled = false;
infoType_listBox.IsEnabled = false;
preloader_Image.Visibility = Visibility.Visible;
}
private void EnableUI()
{
celebrityName_textBox.IsEnabled = true;
submit_Button.IsEnabled = true;
infoType_listBox.IsEnabled = true;
preloader_Image.Visibility = Visibility.Hidden;
}
When I run the application, then press the button, the application crashes immediately!
What's happening ? I tried to use BackgroundWorker Instead, but when I can worker.RunWorkerAsync() nothing happens ( the worker doesn't start ).
DisableUI() is changing the state of UI controls on a thread which is not the UI thread. This is disallowed in WPF, because it is a single threaded UI framework, and only allows you to change controls on something called the UI thread. Dispatcher.Invoke()/BeginInvoke() is your friend.
Good MSDN article on using the Dispatcher
You are going to want to do any UI related stuff on the UI thread, you can do this with the dispatcher, like so:
private void heavyWork()
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(DisableUI));
//rest of method
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnableUI));
}
When using Background Worker your code should look like something like this:
Notice that when you start worker.RunWorkerAsync() method BackgroundWorkerDoWork is called
BackgroundWorker _backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_backgroundWorker.DoWork += BackgroundWorkerDoWork;
_backgroundWorker.RunWorkerCompleted += BackgroundWorkerRunWorkerCompleted;
void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
//DU STUFF HERE
}
void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
/DO STUFF HERE LIKE HIDE / SHOW / ENABLE/ DISABLE buttons
}

Categories

Resources