How to avoid OutOfMemoryException when using Task.Run()? [duplicate] - c#

This question already has answers here:
async/await - when to return a Task vs void?
(6 answers)
Closed 2 years ago.
I get OutOfMemoryException when I call this method on startup. StartSignalR method should run a Task which calls Update() method every second.
public void StartSignalR()
{
Task t = Task.Run(() =>
{
try
{
bool push = true;
while (push)
{
Update();
}
}
catch (System.Exception ex)
{
LogManager.LogError(ex);
}
});
}
I use Task.Delay in Update()
private async static void Update()
{
await Task.Delay(1000);
Updater.MPrice();
}

To make your Task.Delay actually wait, you have to declare your lambda as async and await the Update method.
public void StartSignalR()
{
//added async
Task t = Task.Run(async () =>
{
try
{
bool push = true;
while (push)
{
//await the Update method
await Update();
}
}
catch (System.Exception ex)
{
LogManager.LogError(ex);
}
});
}
With this change your Update method has to return a Task
//changed signature
private async static Task Update()
Chances are good that this should reduce the memory footprint, because currently you are firing the Update method like crazy.

Related

Wait for a specific result from Task then run another method in C#

I have a WPF app running on .net 6 and an external device connected to it.
Initializing the device sometimes fails and I don't want to hold the UI thread trying to initialize it.
I want to run the following method (_device.Init()) in an async fashion and when it returns true, run Start() method.
edit: run it until it returns true from the _device.Init() method, not true for finishing the task
Is there a built-in functionality to do it with tasks? or any other "best practice" way?
Thank you :)
SomeDevice _device = new();
public async void Init()
{
// some other code
while (Task.Run(() => _device.Init()).Result == false)
{
}
Start();
}
public void Start()
{
// some other code
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_device.Start();
}));
}
Instead of getting the Result of the Task (which may block the UI thread) you should await the Task:
public async void Init()
{
// some other code
while (!await Task.Run(() => _device.Init()))
{
}
Start();
}
The method should also be awaitable and be awaited when called, e.g. in an async Loaded event handler:
public async Task Init()
{
// some other code
while (!await Task.Run(() => _device.Init()))
{
}
Start();
}
...
await Init();
public async void Init()
{
var task = _device.Init();
//do work here
await task;
Start();
}
Should do the trick, it'll do the work and then wait for the task to complete before going to Start();
If you want to simply wait for init to finish and then run start it's even simpler with
await _device.Init().ContinueWith((x) => { Start();})

C# Async recursive function not working properly? [duplicate]

This question already has answers here:
async/await - when to return a Task vs void?
(6 answers)
Closed 2 years ago.
This is my code :
class Program
{
static void Main(string[] args)
{
update();
}
static async void update()
{
await Task.Delay(100);
Console.WriteLine("X");
update();
}
}
Console never outputs any text at all, and I have no clue why. What am I doing wrong?
Your Main method is not async, so it doesn't wait for your update method. Also, your update method should return a Task so your Main method can await it.
static async Task Main(string[] args)
{
await update();
}
static async Task update()
{
await Task.Delay(100);
Console.WriteLine("X");
await update();
}

How to make async task run on background?

I have this code with TaskCompletionSource. I need to use in my console app. But the point is that it should be finished in background. If I now use await queries.MarkRandomTaskWithDelay(1000) it waits 1000 ms and then I can continue working with app. But I need to continue working immediately and only then to receive result. How can I do it?
public static class queries
{
public static TaskCompletionSource<int> completionSource = new TaskCompletionSource<int>();
public static Task<int> MarkRandomTaskWithDelay(int interval)
{
Timer timer = new Timer(interval)
{
AutoReset = false
};
timer.Elapsed += Marking;
timer.Start();
return completionSource.Task;
}
private async static void Marking(object o, ElapsedEventArgs e)
{
try
{
await SomeWork();
Console.WriteLine($"\nTask status with ID {task.Id} was changed to 'finished' in background.\n");
completionSource.SetResult(task.Id);
}
catch (Exception ex)
{
completionSource.SetException(ex);
}
}
}
Don't use await until you actually need the result.
//Start the task but don't wait for it to finish
Task<int> task = queries.MarkRandomTaskWithDelay(1000);
//Do whatever work we can do without knowing the result
DoOtherWork();
//Now we need the result, so we await the original task
int result = await task;
UseTheResult(result);

What is the right way to call a Task from MainPage

If await can be used only by async methods, how can I call a task from MainPage()?
My code sample:
public MainPage()
{
InitializeComponent();
label.Text=await Task.Run(TaskTest); //this doesn't work
}
private async Task<string> TaskTest()
{
try
{
using (WebClient client = new WebClient())
{
return await client.DownloadStringTaskAsync("https://www.example.com/return.php");
//also tried w/ no success:
//return client.DownloadStringTaskAsync("https://www.example.com/return.php").Result;
}
}
catch (Exception)
{
throw;
}
}
Avoid async void fire-and-forget methods.
Event handlers however are the only exception to that rule.
Reference Async/Await - Best Practices in Asynchronous Programming
In this case, since you want to await the task then create and event and handler that would facilitate the desired behavior
public MainPage() {
InitializeComponent();
Downloading += OnDownloading; //subscribe to event
Downloading(this, EventArgs.Empty); //raise event to be handled
}
private event EventHandler Downloading = delegate { };
private async void OnDownloading(object sender, EventArgs args) {
//Downloading -= OnDownloading; //unsubscribe (optional)
label.Text = await TaskTest(); //this works
}
private async Task<string> TaskTest() {
try {
using (WebClient client = new WebClient()) {
return await client.DownloadStringTaskAsync("https://www.example.com/return.php");
}
} catch (Exception) {
throw;
}
}
You cannot make the Main() method asynchronous and thus, you can use the await keyword in the body of the Main() function.
A simple workaround that you can implement by editing your current code is making your function TaskTest() return void so you don't have to await it's call.
Example:
public MainPage()
{
InitializeComponent();
TaskTest();
}
private async void TaskTest()
{
try
{
using (WebClient client = new WebClient())
{
label.Text = await client.DownloadStringTaskAsync("https://www.example.com/return.php");
}
}
catch (Exception)
{
throw;
}
}
Edit
In case you have to wait for the return value of an asynchronous call without using await, you could go ahead and use a while to check whether the Task has completed or not.
Task<string> accessTokenTask = Task.Run<string>(() => MethodToGetToken());
// wait until operation is done.
while(!accessTokenTask.IsCompleted)
{
accessTokenTask.Wait():
}
// once the task completes, the runtime will step out of the while loop
// and you can access your Token in the Result
string token = accessTokenTask.Result;
Hope this answers your question.
You probably shouldn't call your Task from MainPage. I started with the Visual Studio blank page and tried to do the same thing. I found an answer suggested to use await Navigation.PushModalAsync(NewPage);, and then call the task there Task.Run(async () => { await method(); }).Wait();. It worked, but not the best way to do it.
This article on CodeProject is great to help beginners to add MVVM to the blank page project. You just need to bind the ViewModel to the MainPage, and then call your Task from the ViewModel instead.
public MainPage()
{
InitializeComponent();
this.BindingContext = new MainPageViewModel(this);
}

Exiting from async infinite loops

I have initiated some async infinite loops in my WinForm application, but each time I am trying to break out of them, the program hangs up. I have read some similar topics where people suggested using CancellationTokens, but I am not able to adapt them to my needs. Here is the relevant part of my code.
static bool processStop = false;
static bool processStopped = false;
//Called once
private async void ProcessData()
{
while (!processStop)
{
await Task.Run
(
() =>
{
//Do stuff and call regular not async methods
}
);
}
processStopped = true;
}
//Button click handler to exit WinForm
btnExit.Click += (senders, args) =>
{
processStop = true;
//Programm hangs up here
while (!processStopped);
FormMain.Close();
}
Edited the code
The variables are static.
The Close method is the default Close() method for Forms.
The problem is that the call to Task.Run continues on the main thread. processStop = true; and while (!processStopped); execute synchronously one after the other. This doesn't let the ProcessData method continue its execution and a deadlock occures.
I see a couple of solutions:
Use ConfigureAwait(false) with Task.Run:
private async void ProcessData()
{
while (!processStop)
{
await Task.Run
(
() =>
{
//Do stuff and call regular not async methods
}
).ConfigureAwait(false);
}
processStopped = true;
}
This will cause the ProcessData to continue on a thread pool and you already use a thread pool by calling Task.Run, so it is not a great solution
Wrap the whole process in Task.Run:
static volatile bool processStop = false;
static volatile bool processStopped = false;
//Called once
private async void ProcessData()
{
await Task.Run(() =>
{
while (!processStop)
{
...
}
processStopped = true;
});
}
This would require changing the form of the method passed to work with the loop in it.
Make ProcessData a synchronous method to process CPU-intensive tasks and call it properly. CancellationToken would be the preferred way to cancel the task:
private void ProcessData(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// do work
}
}
And call it with this:
Task processingTask;
CancellationTokenSource cts;
void StartProcessing()
{
cts = new CancellationTokenSource();
processingTask = Task.Run(() => ProcessData(cts.Token), cts.Token);
}
btnExit.Click += async (senders, args) =>
{
cts.Cancel();
try
{
await processingTask;
}
finally
{
FormMain.Close();
}
}
If you want to spin a bunch of tasks without blocking you can do this:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Called once
private async Task ProcessData()
{
int count = 0;
while (true)
{
await Task.Run
(
() =>
{
this.Invoke(new Action(() => {
label2.Text = (count++).ToString();
label1.Text = DateTime.Now.ToString(); }));
Thread.Sleep(100);
}
);
}
Debugger.Break(); //you will never see this hit at all
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private async void button2_Click(object sender, EventArgs e)
{
await ProcessData();
}
}
}

Categories