I have a console application where in some instances a user interface needs to be presented. This user interface needs to remain responsive as it will contain a loading gif, progress bar, cancel button etc. I have the following sample code:
class Program
{
static void Main(string[] args)
{
DoWork().GetAwaiter().GetResult();
}
private static async Task DoWork()
{
TestForm form = new TestForm();
form.Show();
string s = await Task.Run(() =>
{
System.Threading.Thread.Sleep(5000);
return "Plop";
});
if (s == "Plop")
{
form.Close();
}
}
}
I would expect from the code above for the TestForm to be displayed for approximately 5 seconds before being closed due to the value of the string being "Plop", however all that happens is the Task is run and the if statement is never reached. Furthermore the UI of the TestForm does not remain responsive. What is wrong with this code?
So I've managed to hack together a dirty solution for this. It is not a clean solution so I'm still open to suggestions but for what I need it works fine
private static void DoWork()
{
TestForm form = new TestForm();
Task formTask = Task.Run(() => form.ShowDialog());
Task<string> testTask = Task.Run(() =>
{
for (int i = 1; i < 10; i++)
{
Thread.Sleep(1000);
Console.WriteLine(i.ToString());
}
Console.WriteLine("Background task finished");
return "Plop";
});
Console.WriteLine("Waiting for background task");
testTask.Wait();
if (testTask.Result == "Plop")
{
Dispatcher.CurrentDispatcher.InvokeAsync(() => form.Close());
}
Console.WriteLine("App finished");
}
This outputs 'Waiting for background task' first, followed by the number count of the Task and then outputs 'Background task finished' when the long process is complete, as well as closes the responsive UI form
Its a classic deadlock.When your code hit await ,control goes back to main thread which is a blocking wait for DoWork GetResult(); When Task.Run thread is finished controls tries to go back to main thread but its waiting for DoWork to be finished. That is the reason last If statement never executes.
But apart from deadlock ,there is also one more issue in your code which will make your UI freeze.Its the form.Show() method.If you remove everything related to async-await and only use form ,it will still freeze.The problem is Show method expects a windows message loop which will be provided if you create a Windows.Forms application but here you are launching form from console application which doesnt have a message loop. One solution would be to use form.ShowDialog which will create its own message loop. Another solution is to use System.Windows.Forms.Application.Run method which provides a win messages loop to the form created through thread pool thread. I can give you one possible solution here but its up to you how you structure your code as the root cause is identified.
static void Main(string[] args)
{
TestForm form = new TestForm();
form.Load += Form_Load;
Application.Run(form);
}
private static async void Form_Load(object sender, EventArgs e)
{
var form = sender as Form;
string s = await Task.Run(() =>
{
System.Threading.Thread.Sleep(5000);
return "Plop";
});
if (s == "Plop")
{
form?.Close();
}
}
Ok I did mark my first answer to be deleted, since what I put there works for WPF and not for you require, BUT in this one is doing what you asked, I did try it and opens the WinForm then closes after 5 seconds, here is the code:
static void Main(string[] args)
{
MethodToRun();
}
private static async void MethodToRun()
{
var windowToOpen = new TestForm();
var stringValue = String.Empty;
Task.Run(new Action(() =>
{
Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
windowToOpen.Show();
}).Wait();
System.Threading.Thread.Sleep(5000);
stringValue = "Plop";
Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
if (String.Equals(stringValue, "Plop"))
{
windowToOpen.Close();
}
}).Wait();
})).Wait();
}
Related
I'm trying to update a status message while waiting for a web response to be returned. The call posts files to a server and sometimes it can take 30+ seconds.
I want to update the message (windows form textbox text) if the call is taking longer than expected. If the call has been waiting for 15 seconds, update the message to "This is taking awhile but should complete soon."
I've tried:
async fire and forget
timer using invoke
task.run
both tasks as async, awaiting the web calling Tasks
Background Worker using dowork and progress work
Nothing seems to work. Is it even possible to update the main thread while a task has a thread locked up?
I'm testing with simple calls:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (!worker.CancellationPending)
{
Task.Delay(1000).Wait();
this.Invoke((MethodInvoker)delegate
{
this.box.Text += '.';
this.box.Update();
});
}
}
private void MakeCall()
{
worker.RunWorkerAsync();
using (WebClient client = new WebClient())
{
//Just runs Task.Delay(10000) then returns "Complete"
var res = client.DownloadString("https://localhost:44343/api/TestDelay");
MessageBox.Show(res);
}
worker.CancelAsync();
}
private void button_Click(object sender, EventArgs e)
{
MakeCall();
}
I think I see your problem. You are downloading on the UI thread without ever getting off of it, so the background worker can never get on it either.
Try this code out:
EDIT: Using two Tasks rather than a background worker
private void MakeCall()
{
// it'd be a good idea to disable the button here
ManualResetEventSlim waiter = new ManualResetEventSlim(false);
Task.Run(async () =>
{
while(!waiter.IsSet)
{
await Task.Delay(1000);
this.Invoke((MethodInvoker)delegate
{
this.box.Text += '.';
this.box.Update();
});
}
});
Task.Run(() =>
{
using (WebClient client = new WebClient())
{
var res = client.DownloadString("https://localhost:44343/api/TestDelay");
MessageBox.Show(res);
}
waiter.Set();
// hop back on the UI thread and re-enable your button here
});
}
private void button_Click(object sender, EventArgs e)
{
MakeCall();
}
I'm providing an additional answer here because although the answer by outbred worked in my test, it didnt work in my original program because the form needed to remain open, locking the main form, run one of the defined tasks, then close automatically. That meant the async fire and forget method wasn't an option.
What I did was overloaded the forms ShowDialog method to take an async action, trigger it, show the dialog, then close the dialog on complete.
This method works perfectly, locking the parent and allowing the background worker to update the text.
internal DialogResult ShowDialog(Action action) => ShowDialog(async () => await Task.Run(action));
internal DialogResult ShowDialog(Func<Task> action)
{
action.Invoke().ContinueWith(task => this.DialogResult = DialogResult.OK);
return this.ShowDialog();
}
Then you can call it using one of the following ways:
using (Form1 form = new Form1())
form.ShowDialog(form.MakeCall);
using (Form1 form = new Form1())
form.ShowDialog(() => { form.MakeCall("HelloWorld");} );
using (Form1 form = new Form1())
form.ShowDialog(async () => { await form.MakeCallAsync("HelloWorld");} );
It will display the form as a dialog (locking the parent), run the task to completion, then close the form.
I haven't seen any posts pertaining to my issue, so I apologize if I post a question already asked.
I have a windows form program, c#, that checks stocks and does analysis. The main form launches another form, via a new thread and ShowDialog. While it's loading, it's running a parallel.foreach. In that parallel.foreach, I'd like to show progress on the main form.
I've run into cross-threading issues, and added invoke, although it doesn't appear to be thread-safe as it seems to be deadlocking toward the end of the parallel.foreach. I've tried delegates, events, no luck. Help me Obi-Wans, you're my only hope!
Stripped down version:
Main form
private void button1_Click(object sender, EventArgs e)
{
YearLows yearLows = new YearLows();
Thread yearLowsThread = new Thread(() => StartYearLows(yearLows));
yearLowsThread.Start();
btnGetYearLows.Enabled = false;
}
private void StartYearLows(YearLows yearLows)
{
yearLows.ShowDialog();
}
public void UpdateProgress(string text)
{
lblProgress.Text = text;
}
2nd form dialog
public partial class YearLows : Form
{
private void YearLows_Load(object sender, EventArgs e)
{
// work
Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
});
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => frmMain.UpdateProgress(text)));
}
}
Update 1:
If I move the progress update label to the child form, it appears I am getting all the progress updates. I had to move from the Load event to the Shown event so that the form renders, so users can see the progress updates. I had to follow SLaks advice though and run Task.Run(() => Parallel.ForEach. This will work for me. Would still like to figure out why it still locks up toward the end if I wanted the progress updates on the main form. (I've always read async void was bad, but I guess no way around this in these defined method signatures in winforms)
public partial class YearLows : Form
{
private async void YearLows_Shown(object sender, EventArgs e)
{
await AnalyzeStocks();
}
private async Task AnalyzeStocks(object sender, EventArgs e)
{
// work
await Task.Run(() => Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
}));
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => lblProgress.UpdateProgress(text)));
}
}
Parallel.ForEach is a blocking call; it runs delegates on the calling thread too. Therefore, the UI cannot update until it finishes.
Instead, you should use await with Task.WhenAll (if you're doing async work) or Task.Run(() => Parallel.ForEach(...)) (if it's CPU-bound) so that you leave the UI thread idle and able to update.
you can use Async Await function for this puprose... this link can be more useful to you...
PictureBox animation freezing while Task running
As per SLaks answer, an example of using Task.Run, with UI update
var tasks = new List<Task>();
foreach (var result in results)
{
tasks.Add(Task.Run(async () => {
// DO WORK
Dispatcher.Invoke(() =>
{
// UPDATE THE UI, I.E. ProgressBar.Value++;
});
}));
}
await Task.WhenAll(tasks);
Background
Currently working on a windows form app which I asked to create. I have ran into an issue where the UI freezes when a resource intensive process is being called. I am currently using threading from which I understand is used to prevent the UI from freezing and taking over the entire pc.
Question
Currently when I am using threading to call a method in my base class which is to open a file that is located on a remote server. This method has a delay of approximately 30 to 45 seconds. I am creating my background thread and invoking it to start. When invoked to start if fires, however when it fired it would not wait for my thread to complete basically giving me a null exception. So after some digging I found that in order to wait for the thread to complete you had to invoke the .Join(). However when the Join is invoked it froze my UI completely. So my ingenuity tried to create a work around and created a while loop that would until the thread is no longer alive and continue. However, this also froze the UI. So am I missing something? That is not mention in MSDN Doc
Code Sample
class BaseClass
{
public CWClient ClientFileContext(string clientFile, bool compress)
{
Client clientContext = null;
try
{
if (compress == true)
{
clientContext = appInstance.Clients.Open2(clientFile, superUser, passWord, OpenFlags.ofCompressed);
}
else
{
clientContext = appInstance.Clients.Open2(clientFile, superUser, passWord, OpenFlags.ofNone);
}
}
catch (Exception ex)
{
//TODO
}
return clientContext;
}
}
public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
BaseClass wpSec = new BaseClass();
CWClient client = null;
Thread backgroundThread = new Thread(
new ThreadStart(() =>
{
client = wpSec.ClientFileContext(selectedFileFullPath, true);
}
));
backgroundThread.Start();
//backgroundThread.Join(); << Freezes the UI
var whyAreYouNotWorking = "Stop";
}
}
Work around I tried
while (backgroundThread.IsAlive == true)
{
for (int n = 0; n < 100; n++)
{
Thread.Sleep(500);
progressBar1.BeginInvoke(new Action(() => progressBar1.Value = n));
}
}
// This also freezes the UI
I would also look into the async and await pattern for this. Explained in this post: Using async await still freezes GUI
Your code should be similar to this (Baseclass doesn't change) :
public partial class Form1 : Form
{
private async void button1_Click(object sender, EventArgs e)
{
BaseClass wpSec = new BaseClass();
CWClient client = await Task.Run(() =>
{
return wpSec.ClientFileContext(selectedFileFullPath, true);
}
);
var whyAreYouNotWorking = "Stop";
}
}
This is back-of-the-envelope stuff, but hopefully that gives the basic idea of launching a task, then awaiting the result in an async method. If you don't need your BaseClass hanging around, that can be in the lambda too, leaving you only what you really want.
That link from #Chris Dunaway above is also excellent. http://blog.stephencleary.com/2013/08/taskrun-vs-backgroundworker-round-3.html
Edit: As #BradlyUffner mentions, this is also one of the few times you should use async void and should rather prefer returning Task or Task<T> in virtually all other circumstances.
I am trying to write an application which transfers data between 2 systems. This application is used by a user, so it is WinForm application. When data transfering is started by a click of the user, the GUI gets frozen even though I start the data transfering in another thread. I am doing something wrong but I couldnt figure it out. here is my SIMPLIFIED code below....
What am I doing wrong?
// Button Click Event
private void btnStart_Click(object sender, EventArgs e)
{
StartThread();
}
// This starts the threaad.
public static void StartThread()
{
string msg = string.Empty;
int i = 0;
continue_ = true;
if (list != null)
{
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
}
}
// This is a simplified code.
public static void Test()
{
string msg = string.Empty;
int i = 0;
continue_ = true;
while (continue_)
{
i++;
Thread.Sleep(5000);
FormMain.dal.ExecuteQuery("INSERT INTO A_TEST VALUES('"+i+"')",null,CommandType.Text,out msg);
}
}
Your StartThread() method includes a Thread.Sleep(5000) ... this is happening in your button click method, thus is making the UI thread sleep. Also, it looks like you have an infinite loop on the UI thread as continue_ never gets set to false
I'm guessing what you're trying to achieve here, but this may help:
public static void StartThread()
{
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
Let's have a look at this block in StartThread:
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
You have a while loop dependen on continue_, but you never change it to false. So you get first of all an infinite loop, which causes the GUI to freeze.
why you are modifying i, but never using it, so just remove it.
You don't need also Thread.Sleep(5000);. However, if you really want to wait a time period, you can use an async delay. It will give the GUI free, so that the GUI works until the delay is finished. But for this, you have to declare StartThread as async.
In your:
if (list != null)
{
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
}
You use Thread.Sleep(5000);
This however still targets your main thread.
I would suggest you to remove this line.
Also, why do you use the variable 'i' while you never use it?
I need to run an infinite while loop when a form application starts. A form starts like this:
public Form1()
{
InitializeComponent();
}
Now I want to run another function which will have an infinite loop inside with one second sleep time:
public void doProcess(){
while(true){
Thread.Sleep(1000);
// other task
}
}
How can I do this? When I call doProcess() in the constructor, it does not show the form. I tried to run the while loop for 10 iterations. The form showed up only after all the iterations are finished. I don't understand why it is happening.
You can start a new thread like this:
new Thread(() =>
{
while (true)
{
Thread.Sleep(1000);
//other tasks
}
}).Start();
Although I suggest you read up on threading before you do. If you want to update the form from a different thread you should use: Form.Invoke().
For example: w is the form
w.Invoke((MethodInvoker) delegate
{
w.Width += 100;
});
In short, you are blocking the UI Thread with this infinite loop.
Run it async:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BeginWork();
}
private async void BeginWork()
{
while (true)
{
// Since we asynchronously wait, the UI thread is not blocked by the file download.
var result = await DoWork(formTextField.Text);
// Since we resume on the UI context, we can directly access UI elements.
formTextField.Text = result;
}
}
private async Task<string> DoWork(object text)
{
// Do actual work
await Task.Delay(1000);
// Return Actual Result
return DateTime.Now.Ticks.ToString();
}
}
A while(true) can be a bit excessive for an update loop. May I recommend that you potentially use a Timer, and/or leverage Cancellation Tokens to eagerly cancel requests which have taken too long as to not update UI with potentially stale results in high performance scenarios.
E.g.
public partial class Form1 : Form
{
private readonly Timer _sampleTimer;
public Form1()
{
InitializeComponent();
_sampleTimer = new Timer
{
Interval = 500 // 0.5 Seconds
};
_sampleTimer.Tick += DoWorkAndUpdateUIAsync;
}
private async void DoWorkAndUpdateUIAsync(object sender, EventArgs e)
{
// Since we asynchronously wait, the UI thread is not blocked by "the work".
var result = await DoWorkAsync();
// Since we resume on the UI context, we can directly access UI elements.
resultTextField.Text = result;
}
private async Task<string> DoWorkAsync()
{
await Task.Delay(1000); // Do actual work sampling usb async (not blocking ui)
return DateTime.Now.Ticks.ToString(); // Sample Result
}
private void startButton_Click(object sender, EventArgs e)
{
_sampleTimer.Start();
}
private void stopButton_Click(object sender, EventArgs e)
{
_sampleTimer.Stop();
}
}
It's happening because the ctor never exits and so the form cannot be shown - is this not obvious?
If you want to run a forever/sleep loop line this, you must thread it off.
Do not wait in GUI event handlers, (or ctors).
Can you not use forms.timer?
You are blocking the UI thread. Therefore, the UI cannot be processed as long as doProcess runs.
If you use .Net 4.5, you can use async waits:
public async void doProcess(){
while(true){
await Task.Delay(1000);
// other task
}
}
The cleaner solution would be to use a timer that fires an event every 1 second. You can turn off the timer after 10 loops.
You didn't exit the constructor so the form won't show.
If you want to do it after form shows place your code in Form_Load event.
But you rather want to do it using background thread so you can use backgroundworker
You could place it after the Initialize component, or find the load event of the form and paste your code in there