Trying to start a dialog after bool task, application stucks - c#

When i'm using async task result inside bool button application is stuck
async Task<bool> task1()
{
await Task.Run(() =>
{
for (int a = 0; a <= 1000000000; a++)
{
}
});
return true;
}
private void Start_Click(object sender, EventArgs e)
{
setDialog(true);
if (task1().Result==true)
setDialog(false);
}
private void setDialog(bool show)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetView(Resource.Layout.layout1);
Dialog dialog = builder.Create();
dialog.SetCancelable(false);
if (show) dialog.Show();
else dialog.Dismiss();
}
When i'm pressing start button click my application is stuck and doesnt let me do anything.
If i will use task inside button event then everything is going fine

You deadlocked your application. This: task1().Result waits synchronously and defeats the purpose of tasks. Use async/await like this instead:
private async void Start_Click(object sender, EventArgs e)
{
setDialog(true);
var result = await task1();
if (result)
setDialog(false);
}

Related

Run and Wait for async Task to complete in a Windows Form Application

How can I wait for an async task to complete without freezing the whole Application?
This function works but Cout() gets called while the File is still downloading.
private void Btn_test_Click(object sender, EventArgs e)
{
var task = Task.Run(async () => { await DownloadWebFile("https://speed.hetzner.de/100MB.bin", AppDomain.CurrentDomain.BaseDirectory + "//100MB.bin"); });
Cout(DownloadSuccessMsg);
}
when I do this the whole Application freezes:
private void Btn_test_Click(object sender, EventArgs e)
{
var task = Task.Run(async () => { await DownloadWebFile("https://speed.hetzner.de/100MB.bin", AppDomain.CurrentDomain.BaseDirectory + "//100MB.bin"); });
task.Wait();
Cout(DownloadSuccessMsg);
}
How can I wait correctly before running other code depending on the downloaded file?
private static async Task DownloadWebFile(string url, string fullPath)
{
using var client = new DownloadManager(url, fullPath);
client.ProgressChanged += (totalFileSize, totalBytesDownloaded, progressPercentage) =>
{
SetProgressBarValue((int)progressPercentage);
};
await client.StartDownload();
}
You can mark the method as async void. Returning void from an asynchronous method is usually not a great idea, but in the case of an event handler it's usually considered acceptable.
private async void Btn_test_Click(object sender, EventArgs e)
{
await DownloadWebFile("https://speed.hetzner.de/100MB.bin", AppDomain.CurrentDomain.BaseDirectory + "//100MB.bin");
Cout(DownloadSuccessMsg);
}

How to call a function after thread finished?

I try to call a function after thread finished but I can't .
I only can use while(threadName.isAlive) method before my function caller code , but it's not good because the program stops when i use this code . have you any idea ?
public partial class Form1 : Form
{
Thread myThread;
string myString = string.Empty;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
myThread = new Thread(write);
myThread.Start();
while (myThread.IsAlive) ;
textBox1.Text = myString;
}
public void write()
{
for (int i = 0; i < 10; i++) {
myString += "aaa " + i + "\r\n";
Thread.Sleep(1000);
}
}
}
If you must attach to a Thread rather than a Task then you can just start a task to wait for the thread to exit and then run some additional code, like this:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
static class Program
{
static void Main()
{
Thread thread = new Thread(work);
thread.Start();
Task.Run(() =>
{
thread.Join();
Console.WriteLine("Run after thread finished");
});
Console.ReadLine();
}
static void work()
{
Console.WriteLine("Starting work");
Thread.Sleep(1000);
Console.WriteLine("Finished work");
}
}
}
However, the modern way to approach this is to use Task, await and async.
For example:
async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "Awaiting task";
await writeAsync();
textBox1.Text = "Task finished";
}
Task writeAsync()
{
return Task.Run(() => write());
}
void write()
{
Thread.Sleep(10000);
}
If you try this second approach, you'll see that the UI remains responsive while the textbox says "Awaiting task".
Also note that normally you'd want to stop the user from being able to press the button again while the task is being awaited, to avoid multiple tasks being run. The easiest way to do that is to disable the button while the task is active like so:
async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
textBox1.Text = "Awaiting task";
await writeAsync();
textBox1.Text = "Task finished";
button1.Enabled = true;
}
Switch to Task from Thread and let .Net do the (low level) work for you:
public async Task<string> write() {
string myString = string.Empty;
for (int i = 0; i < 10; i++) {
myString += "aaa " + i + "\r\n";
await Task.Delay(1000);
}
return myString;
}
private async void button1_Click(object sender, EventArgs e) {
string result = await write();
// continue with (please, notice await) with assigning
textBox1.Text = result;
}

Background task in UWP

I want to use a background task for my UWP app.
The below code, is my back button click event in windows mobile:
private async void MainPage_BackRequested(object sender, BackRequestedEventArgs e)
{
var access= await BackgroundExecutionManager.RequestAccessAsync();
var task = new BackgroundTaskBuilder
{
Name="My task",TaskEntryPoint=typeof(backGroundTask.Class1).ToString()
};
trigger = new ApplicationTrigger();
task.SetTrigger(trigger);
task.Register();
//var result = await trigger.RequestAsync();
if (Frame.CanGoBack)
{
Frame.GoBack();
e.Handled = true;
}
}
public void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral();
clearData();
count1 = 0;
getDownloadedSongs();
dispatcherTimer1.Tick += DispatcherTimer1_Tick;
dispatcherTimer1.Interval = new TimeSpan(0, 0, 3);
dispatcherTimer1.Start();
_deferral.Complete();
}
DispatcherTimer dispatcherTimer1 = new DispatcherTimer();
private async void DispatcherTimer1_Tick(object sender, object e)
{
try
{
clearData();
}
catch (Exception ex)
{
}
}
But this code is not working, when I click back button. As per expectation background task code should work, but it is not working. What am I doing wrong?
Your background task is exiting before the DispatcherTimer gets a chance to ever execute, because you mark the Deferral as complete. You should hold on to the Deferral until all the work in your background task has been completed (or until you receive a TaskCanceled event from the system).

Multi threading approach

I have developed the below code using TPL and all works fine except how to restart the task. Currently when I click button1 the thread starts in textBox1 and similarly with button3 and textBox2. When I click button3 and button4 both the threads get's cancelled.
Now I want to restart the thread where left off on the click event of button1 and button3. I tried many approaches but could not get any idea on how to do this.
Here's the code:
public partial class Form1 : Form
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token;
CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
CancellationToken token1;
public Form1()
{
InitializeComponent();
token = cancellationTokenSource.Token;
token1 = cancellationTokenSource1.Token;
}
private void button1_Click(object sender, EventArgs e)
{
Task t = Task.Factory.StartNew(() =>
{
var run = true;
while (run)
{
for (int i = 0; i < 100; i++)
{
if (token.IsCancellationRequested)
{
run = false;
break;
//token.ThrowIfCancellationRequested();
}
// your code
System.Threading.Thread.Sleep(1000);
Action act = () => textBox1.Text = Convert.ToString(i);
textBox1.Invoke(act);
}
}
});
}
private void button2_Click(object sender, EventArgs e)
{
cancellationTokenSource.Cancel();
}
private void button3_Click(object sender, EventArgs e)
{
Task t1 = Task.Factory.StartNew(() =>
{
var run = true;
while (run)
{
for (int i = 0; i < 100; i++)
{
if (token1.IsCancellationRequested)
{
run = false;
break;
//token1.ThrowIfCancellationRequested();
}
// your code
System.Threading.Thread.Sleep(1000);
Action act = () => textBox2.Text = Convert.ToString(i);
textBox2.Invoke(act);
}
}
});
}
private void button4_Click(object sender, EventArgs e)
{
cancellationTokenSource1.Cancel();
}
}
You need to sync thread using signaling, generally they are achieved using Event Wait Handles like AutoResetEvent, ManualReset and CountdownEvent. You can achieve this with ManualReset.
Declare ManualResetEvent:
CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
CancellationToken token1;
private ManualResetEvent mre = new ManualResetEvent(false);
Modify on CancellationRequest
if (token.IsCancellationRequested) {
run = false;
mre.WaitOne();
//token.ThrowIfCancellationRequested();
}
OnStart/OnResume: mre.Set();

Enabling responsive GUI while processing

I have a lengthily processing in my winform when I click a button; namely, i'm loading lots of files and processing them. For the duration of the processing, my GUI is frozen and unresponsive which is a problem since the processing can take an upward of 10 minutes. Is there a way of putting the code in some sort of bubble or something so I can use the GUI while processing the files? Maybe even add the "Cancel" button.
EDIT: René's solution works, also here's progressbar control I also wanted:
private async void button1_Click(object sender, EventArgs e)
{
progressBar1.Maximum = ValueWithTOtalNumberOfIterations.Length;
IProgress<int> progress = new Progress<int>(value => { progressBar1.Value = value;});
await Task.Run(() =>
{
var tempCount = 0;
//long processing here
//after each iteration:
if (progress != null)
{
progress.Report((tempCount));
}
tempCount++;
}
}
You could simply make your button's click handler async and start a Task for your long running operation:
public async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false; // disable button to not get called twice
await Task.Run(() =>
{
// process your files
}
button1.Enabled = true; // re-enable button
}
The compiler turns this into a state machine. The control flow is returned to the caller (the UI) at the await keyword. And execution of this method is resumed when your Task has completed.
To use a "Cancel"-Button you can use a TaskCancellationSource or simply define a flag that you check while you're processing your files, and return if the flag is set (by the click handler of your "Cancel" button):
private bool _stop = false;
private void cancelButton_Click(object sender, EventArgs e)
{
_stop = true;
}
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false; // disable button to not get called twice
_stop = false;
await Task.Run(() =>
{
// process your files
foreach(var file in files)
{
if (_stop) return;
// process file
}
}
button1.Enabled = true; // re-enable button
}

Categories

Resources