Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have just started to learn asynchronous coding and have run into a problem.
So this is what I am trying to do.
I have a method which calls two awaited methods asynchronously. For testing purposes each method is calling a stored procedure which executes WAITFOR DELAY 'xxx'.
Within each method call another method needs to run alongside it to generate three loading dots appended to the text value passed in.
So when await Task.Run(() => DisableReplication("Disable Replication")) is running (calling the stored procedure for say 10 secs) it will display
Disabling Replication.
Disabling Replication..
Disabling Replication...
until that stored procedure has finished, then it should say
Disabling Replication - Complete
Then it should call await Task.Run(() => ImportWeights("Importing Weights")), where for example the proc takes 20 secs to complete. So it will display
Importing Weights.
Importing Weights..
Importing Weights...
Then once complete it will say
Import Weights - Complete.
Within each method there is a bool value being set from false to true once the method has finished. That value is used in the ShowProgressText method in the while loop. Once the value is set to true it should break out.
At present it is doing the 1st part fine with displaying 'Disabling Replication' but once that method is completed it starts switching between 'Disabling Replication' and 'Importing Weights'. I assumed the Disabling Replication method was finished so I am not sure why it continues to pick the text up. I think the issue is with the ShowProgressText method.
Does anyone known what I am doing wrong and perhaps if I could simplify this somehow?
Here is a .gif showing what it is doing and the code I am using is below.
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DirectMailSpendManagement
{
public partial class FrmRapport : AestheticsFormBase
{
#region Instantiations
private readonly RapportDataAccess access = new RapportDataAccess();
private readonly Panel panel = new Panel();
#endregion
#region Fields
private bool IsCompleted;
#endregion
#region Constructors
public FrmRapport()
{
InitializeComponent();
}
#endregion
#region Private Methods
private void BtnGetRapportFile(object sender, EventArgs e)
{
_ = LoadRapport();
}
private async Task LoadRapport()
{
await Task.Run(() => DisableReplication("Disable Replication"));
await Task.Run(() => ImportWeights("Importing Weights"));
}
private void DisableReplication(string txt)
{
IsCompleted = false;
Task.Run(() => ShowProgressText(txt));
access.DisableReplication();
}
private void ImportWeights(string txt)
{
IsCompleted = false;
Task.Run(() => ShowProgressText(txt));
access.ImportAndCalculateWeights(panel.PanelNumber, "", "", "");
}
private void ShowProgressText(string txt)
{
var count = 0;
var logText = new StringBuilder();
logText.Append(txt);
var baseLen = logText.Length;
while (!IsCompleted)
{
Thread.Sleep(300);
if (count >= 3)
{
logText.Remove(baseLen, count);
count = 0;
}
logText.Append(".");
count++;
BeginInvoke(new Action(() => { UpdateProgressText(logText.ToString()); }));
}
BeginInvoke(new Action(() => { UpdateProgressText(txt + " - Complete"); }));
}
private void UpdateProgressText(string txt)
{
lblProgress.Text = txt;
}
#endregion
}
}
The problem is you have two separate tasks running that call ShowProgressText. Task.Run() is not something you normally use unless you are interfacing with code that does not use the C# async/await pattern. So perhaps LoadRapport could be like this:
bool IsCompleted;
string LogText;
private async Task LoadRapport()
{
LogText = "Disable Replication";
IsCompleted = false;
_ = ShowStatus(); // start this task but don't wait for it to finish.
// Start these long running methods on a separate non UI thread using Task.Run.
await Task.Run(() => DisableReplication());
LogText = "Importing Weights";
await Task.Run(() => ImportWeights());
IsCompleted = true; // tell ShowStatus to complete.
}
and ShowStatus could be like this:
async Task ShowStatus()
{
while (!IsCompleted) {
BeginInvoke(new Action(() =>
{
UpdateProgressText(this.LogText);
})); ;
await Task.Delay(200);
}
UpdateProgressText("Completed");
}
I would also remember to disable the button that calls BtnGetRapportFile during this long running process so the user doesn't click it 10 times and get 10 separate threads running calling your SQL server...
Related
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();
}
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.
The code below is a simplified example of a problem I am having. What happens upon the form loading - the For Loop will create a new task per iteration, then go as far as entering the 'if (pic.InvokeRequired)' section, but will return back to the For Loop and continue to iterate BEFORE any of the Tasks go through their respective invocations of method() after pic.BeginInvoke() is called.
What I am trying to achieve is for the invoking to complete it's second pass through method() and eventually changing the pic.BackColor to Color.Blue. I imagine this is possible, no? I've spent a couple hours searching around and could not find a satisfactory answer to this problem..
To run this code yourself, make a new WinForms Application Project (I'm using Visual Studio 2012) and add a PictureBox called 'pic'. Insert the code below.
Thank you for any and all help!
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
Task task = Task.Factory.StartNew(() => method());
task.Wait(); //Waits for task to complete before proceeding
}
}
public delegate void delegate_method();
private void method()
{
if (pic.InvokeRequired)
{
delegate_method dm = new delegate_method(() => method());
pic.BeginInvoke(dm); //If ran once (without the loop in Form1_Load) it would call 'method()' immediately from here.
}
else
{
pic.BackColor = Color.Blue;
}
}
I can appreciate the question because in some situations blocking may be preferable to full-on async (although it's more of a last resort measure).
The answer here depends on whether your async call (the actual meat of the work which happens in the background before you get to invoke an action on the UI thread - the part of the code which I'm assuming you chose to omit for the sake of simplicity) does any UI-bound work. If it does, you're out of luck and using Application.DoEvents() is your best bet.
If, however, it does not, and you have enough control over the async code, what you can do is instead of trying to invoke the UI from within your task, pass the Action describing the work back to your UI thread (as your Task's Result) and then handle its invocation there.
Note that I've simplified the implementation of Method() as it no longer gets called from non-UI threads.
WARNING: SIMPLIFIED CODE, NOT SUITABLE FOR PRODUCTION.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PicInvoke
{
public partial class Form1 : Form
{
public Form1()
{
this.InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Stopwatch sw;
// Sequential processing.
sw = Stopwatch.StartNew();
this.DoWorkAndBlockSequential();
sw.Stop();
MessageBox.Show(string.Format("Sequential work complete. Time taken: {0:0.000}s.", (double)sw.ElapsedMilliseconds / 1000.0));
// Parallel processing.
sw = Stopwatch.StartNew();
this.DoWorkAndBlockParallel();
sw.Stop();
MessageBox.Show(string.Format("Parallel work complete. Time taken: {0:0.000}s.", (double)sw.ElapsedMilliseconds / 1000.0));
}
private void DoWorkAndBlockSequential()
{
for (int i = 0; i < 5; i++)
{
var task = this.DoWorkAsync();
// Block the UI thread until the task has completed.
var action = task.Result;
// Invoke the delegate.
action();
}
}
private void DoWorkAndBlockParallel()
{
var tasks = Enumerable
.Range(0, 5)
.Select(_ => this.DoWorkAsync())
.ToArray();
// Block UI thread until all tasks complete.
Task.WaitAll(tasks);
foreach (var task in tasks)
{
var action = task.Result;
// Invoke the delegate.
action();
}
}
private Task<Action> DoWorkAsync()
{
// Note: this CANNOT synchronously post messages
// to the UI thread as that will cause a deadlock.
return Task
// Simulate async work.
.Delay(TimeSpan.FromSeconds(1))
// Tell the UI thread what needs to be done via Task.Result.
// We are not performing the work here - merely telling the
// caller what needs to be done.
.ContinueWith(
_ => new Action(this.Method),
TaskContinuationOptions.ExecuteSynchronously);
}
private void Method()
{
pic.BackColor = Color.Blue;
}
}
}
I have a long running task that behaves like a transaction - it involves many operations where success of one depends on success of another.
class MyTransaction
{
public void Execute()
{
StopServices();
BackupFiles();
OverwriteFiles();
BackupDatabases();
RunChangeScripts();
... and few others
}
public void RollBack() { }
}
class MyTransactionManager
{
public RunTransactions()
{
Task.Factory.StartNew(() => {
new MyTransaction().Execute();
});
}
}
This is just a pseudo-code of the real application where different operations are provided by different components of the system. There is an underlying GUI (WinForms) that displays progress using a progress bar and few other thing that have to stay responsive no matter what happens. Transactions are all really long running so there is no need to specify it when starting tasks (using TaskCreationOptions), it always runs in a new thread. Progress from transactions is reported back to the GUI using events.
Now, there is a request that if something during execution of a transaction fails it won't immediately roll back as it currently does. They want to pop up a message box in the GUI giving a user an option to decide whether to roll back or fix the error and continue from the last successful point.
So I need somehow implement a blocking. I thought that I could just raise another event, pop up a message box and say "Hey, fix it and then press ok". And bind that OK click event to my outer manager (public API) which can delegate requests directly to my transactions. And blocking would just actively run a while loop checking some bool property.
Now I am thinking that passive blocking would be better but I don't really know how to do it. Could you please advise me?
EDIT: And I don't really want to use Thread.Sleep, because these errors can take various time to fix. Depends on an error and a person who is fixing it.
And blocking would just actively run a while loop checking some bool property.
That's not blocking, it's called busy waiting and it's something you should avoid.
If you want to have synchronization like this between two threads, one way is to use ManualResetEvent:
AskUser(); // doesn't block
shouldRollbackEvent.WaitOne();
if (shouldRollback) …
And on your UI thread:
shouldRollback = …;
shouldRollbackEvent.Set();
(This assumes both parts of the code execute within the same object.)
May be you can try something like this
private static Task<bool> WaitTillUserInput()
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
uiSynchronizationContext.Post(x =>
{
if (MessageBox.Show("Do you want to rollback...?", "Please confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
tcs.SetResult(true);
}
else
{
tcs.SetResult(false);
}
}, null);
return tcs.Task;
}
C# 5.0
public async void Execute()
{
...Do something
//Encountered an error
var doRollBack = await WaitTillUserInput();
if(doRollBack)
{
//rollback here
}
}
C# 4.0
public void Execute()
{
...Do something
//Encountered an error
var doRollBackTask = WaitTillUserInput();
doRollBackTask.ContinueWith(antecedent =>
{
if (antecedent.Result)
{
//rollback here
}
});
}
EventWaitHandle _wait;
private async void buttonStart_Click(object sender, EventArgs e) {
_wait = new EventWaitHandle(true, EventResetMode.ManualReset);
await Task.Run(() => GoInc());
}
private void buttonPause_Click(object sender, EventArgs e) {
_wait.Reset();
}
private void buttonResume_Click(object sender, EventArgs e) {
_wait.Set();
}
EventWaitHandle _wait;
private void GoInc() {
for (int i = 0; i < 10000; i++) {
_wait.WaitOne();
Thread.Sleep(100);
}
}
I've an existing WPF application, which has several sections. Every section is a UserControl, that implements an interface.
The interface specify two methods: void LoadData([...]) and bool UnloadData().
Those method are called by the UI thread, so we need to do our work in backgroundworker if it's time consuming.
No problems with LoadData since we can update the UI asynchronously. The problem is with UnloadData().
This should return if we can really leave the current view.
This is computed with the current status of data(Saved/modified/Invalid):
Saved return true,
Invalid asks if you want to stay to save some
correct data or leave without saving
Modified tell you that you can
either cancel your change(return true), either continue to
edit(return false), either save you current data(return true)
The problem is with the "Modified -> Save". This is a time consuming method, so to respect the philosophy of the application, we should run this in a background thread(with a busy indicator).
But if we just launch the thread and go to the next section, it will return "true" to the method call, and we will directly launch the next view.
In my case, loading the next view before our local data is saved can be a problem.
So:
Is there a way to wait on the background thread to finish before returning "true", WITHOUT blocking the UI?
public bool UnloadData(){
if(...){
LaunchMyTimeConsumingMethodWithBackgroundWorker();
return true;//Only when my time consuming method ends
}
//[...]
}
Important EDIT
Maybe I wasn't clear enought: I know how to use a BackgroundWorker, or TPL. My problem is that the parent class(the one which call the UnloadData()" is a class that I cannot edit(for multiple reasons: It's in another DLL that will not be reloaded, it already works with 70+ userControls, all in separate projects(dll), loaded by reflection.
This wasn't my choice, I don't find it good, but I've to deal with it now. I'm mostly looking for way to make my method wait on the return of my method. I'm not sure if it is possible. But I'm looking for a workaround, it will spare me weeks of works.
Ok now I'm excited, because I think I may have discovered something on my own...
So, what you do is this: You create a DispatcherFrame, push that frame onto the Dispatcher, and in the RunWorkerCompleted you set the Continue of the Frame to false.
This is the code so far:
public void Function()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += TimeConsumingFunction;
var frame = new DispatcherFrame();
worker.RunWorkerCompleted += (sender, args) =>
{
frame.Continue = false;
};
worker.RunWorkerAsync();
Dispatcher.PushFrame(frame);
}
private void TimeConsumingFunction(object sender, DoWorkEventArgs doWorkEventArgs)
{
Console.WriteLine("Entering");
for (int i = 0; i < 3; i++)
{
Thread.Sleep(1000);
}
Console.WriteLine("Exiting");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Function();
Console.WriteLine("Returns");
}
You should implement a dependency property "IsBusy" of type bool, that you set to TRUE before starting the BackgoundWorker, and then to FALSE when the work is complete.
On the UI, you bind to that property whatever functionality you want disabled during the processing(like the button for loading the next view, etc.); or maybe showing a "Cancel" button.
You should not "wait" for the operation to complete, you can retrieve the result in an additional variable, that the BackgroundWorker will set:
BackgroundWorker _bw;
bool _returnValue = false;
private void button_Click(object sender, RoutedEventArgs e)
{ // if starting the processing by clicking a button
_bw = new BackgroundWorker();
IsBusy = true;
_bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
_bw.RunWorkerAsync();
}
void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
IsBusy = false;
// retrieve the result of the operation in the _returnValue variable
}
void _bw_DoWork(object sender, DoWorkEventArgs e)
{
_returnValue = UnloadData();
}
private bool UnloadData()
{
if (...)
{
LaunchTimeConsumingMethod();
return true;
}
else
return false;
//etc ...
}
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
// Using a DependencyProperty as the backing store for IsBusy. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register( ... )
You may be able to try using the new "await" features of .NET 4.5.
The await keyword allows you to await the completion of a Task object, without blocking the UI.
Try this modification:
public async bool UnloadData()
{
if(...)
{
await Task.Factory.StartNew(() =>
{
LaunchMyTimeConsumingMethod();
});
return true;//Only when my time consuming method ends
}
//[...]
}
Treat UnloadData as a async operation and let the async/await features handle both the case when it completes synchronously and when it needs to complete asynchronously:
public async Task<bool> UnloadData(){
if(...){
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool
await Task.Factory.StartNew(()=>LaunchMyTimeConsumingMethodWithBackgroundWorker());
// The return statement is the continuation and will run in the UI thread after the consuming method is executed
return true;
}
// If it came down this path, the execution is synchronous and is completely run in the UI thread
return false;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
bool result = await this.UnloadData();
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
EDIT
If you can't modify the UnloadData, then just execute it on the ThreadPool, as #BTownTKD noted:
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool, whether it takes the long or the short path
bool result = await The Task.Factory.StartNew(()=>this.UnloadData());
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
You probably should use TPL if your framework version is 4.0:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); // this will work only if you're running this code from UI thread, for example, by clicking a button
Task.Factory.StartNew(() => UnloadData()).ContinueWith(t => /*update ui using t.Result here*/, uiScheduler);
Hope this helps.
You have to implement a callback function (RunWorkerCompleted), this is called when the background worker finishes.
Check out an example here:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx