How to use ContinueWith with condition on existing Task<TResult> - c#

I'm trying to get some help on the .ContinueWith() method.
I know something like thisTask<Task<bool>> t2 = nextTask.ContinueWith(t => T.FirstLevel("GG"));
As you see below, this is a demo of what I was trying to do. My first level task returns a bool that will determine which task to continue with, also my second level task will return a bool to determine whether to go back execute the first level again or exit. This is where I'm stuck, I wouldn't want to make it recursive though.
Can anyone help?
I know one can use event handler to resolve this matter but the actual application code is really complex to change at this moment.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Continuewith
{
class Program
{
static void Main(string[] args)
{
tester();
Console.ReadKey();
}
static async void tester()
{
TaskFunctions T = new TaskFunctions();
List<string> p = new List<string>() { "111"};
List<Task<bool>> CocurrentTasks = new List<Task<bool>>();
foreach (string s in p)
{
CocurrentTasks.Add(T.FirstLevel(s));
}
while (CocurrentTasks.Count > 0)
{
Task<bool> nextTask = await Task.WhenAny(CocurrentTasks);
if(await nextTask)
{
//do second level
}
else
{
//do first level
}
CocurrentTasks.Remove(nextTask);
}
}
}
class TaskFunctions
{
public async Task<bool> FirstLevel(string gg)
{
Console.WriteLine(gg);
Random random = new Random();
int randomNumber = random.Next(0, 10);
await Task.Delay(500 * randomNumber); //other real useful Task Function will be in place to take up the time
if (randomNumber > 5)
return true;
else
return false;
}
public async Task<bool> SecondLevel(string jj)
{
Console.WriteLine(jj);
Random random = new Random();
int randomNumber = random.Next(0, 10);
await Task.Delay(500 * randomNumber); //other real useful Task Function will be in place to take up the time
if (randomNumber > 5)
return true;
else
return false;
}
}
}
Here is my solution, not perfect but I can use it alright.
static async void tester()
{
TaskFunctions T = new TaskFunctions();
List<string> p = new List<string>() { "111"};
List<Task<bool>> CocurrentTasks = new List<Task<bool>>();
foreach (string s in p)
{
CocurrentTasks.Add(T.FirstLevel(s));
}
while (CocurrentTasks.Count > 0)
{
Task<bool> nextTask = await Task.WhenAny(CocurrentTasks);
if(await nextTask)
{
Task<Task<bool>> t2 = nextTask.ContinueWith(t => T.SecondLevel("GG"));
if(!await t2.Unwarp())
{
CocurrentTasks.Add(T.FirstLevel("111"));
}
}
else
{
CocurrentTasks.Add(T.FirstLevel("111"));
}
CocurrentTasks.Remove(nextTask);
}
}
This, however, requires the first level task returns "111" so that the program will be execute that specific data again. "111" can be anything one can pass not just limited to a string.

Why do you need FirstLevel and SecondLevel to be separate Tasks? Maybe it would be easier to control those calls as part of one, sequential Task? Basically, you would want to move code in your while to a method:
static async void tester()
{
TaskFunctions T = new TaskFunctions();
List<string> p = new List<string>() { "111" };
List<Task> CocurrentTasks = new List<Task>();
foreach (string s in p)
{
CocurrentTasks.Add(CallConcurrentTasks(T, s));
}
while (CocurrentTasks.Count > 0)
{
var nextTask = await Task.WhenAny(CocurrentTasks);
CocurrentTasks.Remove(nextTask);
}
}
static async Task CallConcurrentTasks(TaskFunctions t, string arg)
{
do
{
if (await t.FirstLevel(arg))
{
if (!await T.SecondLevel("GG"))
{
return;
}
}
}
while (true); //you should probably make some additional end condition here?
}
I hope I got the refacotring right. Alternatively you could use a thread-safe list for ConcurrentTasks collection and use your while code without any changes (collection passed as an argument to CallConcurrentTasks method).

Related

I can not handle these classes c# uwp

until now I only have small applications, but now I can not handle these classes c# uwp
I want to break the code into several classes, each class will deal with a few parameters. classes will send parameter values to the main page. but when I try to display those parameters, they are always zero, even though the text file appears modified.
i have main page
namespace airflow
{
public sealed partial class MainPage : Page
{
}
public MainPage()
{
}
private async void main_page_Loaded(object sender, RoutedEventArgs e)
{
param_perimetrala read = new param_perimetrala();
ora_start_perimetrala = read.start_perimetrala;
var mesaj = new MessageDialog(ora_start_perimetrala.ToString());
var res = await mesaj.ShowAsync();
}
}
and a class
namespace airflow
{
public class param_perimetrala
{
public static int ora_start_perimetrala;
public int minut_start_perimetrala;
public int ora_stop_perimetrala;
public int minut_stop_perimetrala;
public int ore_ciclu_perimetrala;
public int minut_ciclu_perimetrala;
public int contor_ore_perimetrala = 0;
public int contor_minute_perimetrala = 0;
public int contor_sec_perimetrala = 0;
public async void readfile_perimetrala()
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile perimetrala_file = await folder.CreateFileAsync("parametrii_perimetrala.txt", CreationCollisionOption.OpenIfExists);
var readFile_perimetrala = await FileIO.ReadLinesAsync(perimetrala_file);
int count = 0;
foreach (var line in readFile_perimetrala)
{
string[] split_perimetrala = line.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
var temp = split_perimetrala[1];
if (count == 0)
{
ora_start_perimetrala = Int32.Parse(temp);
}
if (count == 1)
{
minut_start_perimetrala = Int32.Parse(temp);
}
if (count == 2)
{
ora_stop_perimetrala = Int32.Parse(temp);
}
count = count + 1;
}
}
public int start_perimetrala
{
get { return ora_start_perimetrala; }
set { ora_start_perimetrala = value; }
}
}
}
how to send ora_start_perimetrala value in mainpage?
enter image description here
In your main_page_Loaded event handler method you are calling read_file_perimetrala but read_file_perimetrala is async void. This means as soon as you get to an await statement, and the await is actually awaitable (not returned immediately) then the method will put the rest of the work aside until the awaited portion is complete. Then it picks back up and runs the rest. During that time of placing it aside the calling thread then continues to run wherever it's needed.
You need to make this a Task and await the call to is also so that you can insure the parameters are filled before continuing work.
Change your readFile_perimetrala to be like so:
public async Task readfile_perimetralaAsync()
Change the entire main_page_Loaded event handler to read like so...
private async void main_page_Loaded(object sender)
{
param_perimetrala read = new param_perimetrala();
await read.readfile_perimetralaAsync();
var mesaj = new MessageDialog(read.start_perimetrala.ToString());
var res = await mesaj.ShowAsync();
}
In your code you're assigning the value to a method but I can tell that you want the int value.
Just some light weight examples to help get started understanding Task and async await. Don't use this as a resource but just enough to get you curious to dig deeper because it's a simple but rather deep subject.
public class TaskExamples
{
public async void DoAsyncVoid()
{
await Task.Delay(200);
}
public async Task DoAsyncTask()
{
await Task.Delay(200);
}
public async Task<int> DoReturnValueTask()
{
await Task.Delay(200);
return 50;
}
public async void CallingTasks()
{
DoAsyncVoid(); //This can't use await because it is 'void' so the next line is ran as soon as this command reaches the first 'true awaitable' await.
await DoAsyncTask(); //This runs before DoAsyncVoid is complete.
var value = await DoReturnValueTask(); //This waits until 'DoAsyncTask' is complete because it is a Task and awaited.
await new MessageDialog(value.ToString()).ShowAsync(); //This waits until 'DoReturnValueTask' is complete and value will be 50 in this case.
//All code here waits until the Dialog is closed because it is also awaited.
}
}

How to unit test method that uses await delay

I have created dummy code to describe my issue as follows:
public class ItemGenerator
{
public bool isStopped;
public List<int> list = new List<int>();
public void GetItems(int itemsPerSecond)
{
int i = 0;
while (!isStopped)
{
list.add(i);
await Task.Delay(1000);
i++;
}
}
}
[Test]
public void TestGetItmes()
{
ItemGenerator gen = new ItemGenerator();
gen.GetItems(1000);
await Task.Delay(5000).ContinueWith(t =>
{
gen.isStopped = true;
Assert.True(gen.list.Count() == (5 * 1000));
});
}
Now the problem is that the assert will fail sporadically, I guess it's to do with CPU performance and the fact that there is no guarantee that delay of 1000 will be always 1000ms but what would be the best approach to UT this kind of logic ?
Here's how I would approach this - firstly use the built in CancellationToken
public class ItemGenerator
{
public List<int> List { get; } = new List<int>();
public async Task GetItems(CancellationToken token)
{
int i = 0;
while(!token.IsCancellationRequested)
{
List.Add(i);
await Task.Delay(1000);
i++;
}
}
}
Then your test can make use of CancellationTokenSource and specifically CancelAfter method:
var gen = new ItemGenerator();
CancellationTokenSource src = new CancellationTokenSource();
src.CancelAfter(5000);
await gen.GetItems(src.Token);
Note you could pass the CancellationToken in to the constructor of ItemGenerator instead of the method if that is more appropriate.

How to make different calls to an async method waiting for the result of first call?

Let's say I have a method it gets data from server
Task<Result> GetDataFromServerAsync(...)
If there is an ongoing call in progress, I don't want to start a new request to server but wait for the original to finish.
Let's say I have
var result = await objet.GetDataFromServerAsync(...);
and in a different place, called almost at the same time I have a second call
var result2 = await objet.GetDataFromServerAsync(...);
I don't want the second to start a new request to server if the first didn't finish. I want both calls to get the same result as soon as first call finish. This is a proof of concept, I have options but I wanted to see how easy it's to do this.
Here is a quick example using Lazy<Task<T>>:
var lazyGetDataFromServer = new Lazy<Task<Result>>
(() => objet.GetDataFromServerAsync(...));
var result = await lazyGetDataFromServer.Value;
var result2 = await lazyGetDataFromServer.Value;
It doesn't matter if these 2 awaits are done from separate threads as Lazy<T> is thread-safe, so result2 if ran second will still wait and use the same output from result.
Using the code from here you can wrap this up in a class called AsyncLazy<T>, and add a custom GetAwaiter so that you can just await it without the need to do .Value, very tidy =)
You can use anything for syncrhonization in your method.
For example, I used SemaphoreSlim:
public class PartyMaker
{
private bool _isProcessing;
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private DateTime _something;
public async Task<DateTime> ShakeItAsync()
{
try
{
var needNewRequest = !_isProcessing;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest) return _something;
_isProcessing = true;
_something = await ShakeItSlowlyAsync().ConfigureAwait(false);
return _something;
}
finally
{
_isProcessing = false;
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
Usage:
var maker = new PartyMaker();
var tasks = new[] {maker.ShakeItAsync(), maker.ShakeItAsync()};
Task.WaitAll(tasks);
foreach (var task in tasks)
{
Console.WriteLine(task.Result);
}
Console.WriteLine(maker.ShakeItAsync().Result);
Here is result:
17.01.2017 22:28:39
17.01.2017 22:28:39
17.01.2017 22:28:41
UPD
With this modification you can call async methods with args:
public class PartyMaker
{
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private readonly ConcurrentDictionary<int, int> _requestCounts = new ConcurrentDictionary<int, int>();
private readonly ConcurrentDictionary<int, DateTime> _cache = new ConcurrentDictionary<int, DateTime>();
public async Task<DateTime> ShakeItAsync(Argument argument)
{
var key = argument.GetHashCode();
DateTime result;
try
{
if (!_requestCounts.ContainsKey(key))
{
_requestCounts[key] = 1;
}
else
{
++_requestCounts[key];
}
var needNewRequest = _requestCounts[key] == 1;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest)
{
_cache.TryGetValue(key, out result);
return result;
}
_cache.TryAdd(key, default(DateTime));
result = await ShakeItSlowlyAsync().ConfigureAwait(false);
_cache[key] = result;
return result;
}
finally
{
_requestCounts[key]--;
if (_requestCounts[key] == 0)
{
int temp;
_requestCounts.TryRemove(key, out temp);
_cache.TryRemove(key, out result);
}
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
public class Argument
{
public Argument(int value)
{
Value = value;
}
public int Value { get; }
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

Encapsulate a task to record duration of an async function

If I start with a full console app:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
internal class Program
{
private static void Main(string[] args)
{
var tas = new List<TaskAuditor<string>>();
// I want to await these when they actually run so another thread can use it while
// wait for (example) my EF/SQL to run Async
tas.Add(new TaskAuditor<string>(await GetName(string.Empty, 1)));
tas.Add(new TaskAuditor<string>(await GetName(string.Empty, 2)));
var running = new Task<String>[2];
foreach (var ta in tas)
{
running[0] = ta.Start();
}
var runningCount = tas.Count;
while (runningCount > 0)
{
var idx = Task.WaitAny(running);
runningCount--;
var task = running[idx];
var ta = tas.FirstOrDefault(t => t.Task == task);
Console.WriteLine(ta.Duration.ToString());
}
foreach (var ta in tas)
{
Console.WriteLine(ta.Task.Result);
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
public async Task<string> GetName(string constr, int id)
{
string result = id.ToString();
// EF/SQL Async goes here
if (string.IsNullOrWhiteSpace(constr))
{
await Task.Delay(1000 * id);
}
return result;
}
}
public class TaskAuditor<T>
{
private Task<T> _task;
private Stopwatch _sw = new Stopwatch();
public TaskAuditor(Task<T> task)
{
_task = task;
}
public Task<T> Start()
{
_sw.Start();
_task.Start();
_sw.Stop();
return _task;
}
public TimeSpan? Duration()
{
TimeSpan? result = null;
if (!_sw.IsRunning)
{
result = _sw.Elapsed;
}
return result;
}
public Task<T> Task
{
get
{
return _task;
}
}
}
}
DotNetFiddle Sample.
The problem is that I need to await the method and it turns into an Async nightmare I can't quite figure out.
First, your auditor needs some work. As I describe on my blog, you can't call Start on a Promise Task. If you want to represent an operation that results in a task, then use Func<Task<T>>. The next problem is that you're stopping the stopwatch before the task actually completes:
public class TaskAuditor<T>
{
private Func<Task<T>> _func;
private Stopwatch _sw = new Stopwatch();
public TaskAuditor(Func<Task<T>> func)
{
_func = func;
}
public async Task<T> StartAsync()
{
_sw.Start();
try
{
return await _func();
}
finally
{
_sw.Stop();
}
}
public TimeSpan? Duration()
{
TimeSpan? result = null;
if (!_sw.IsRunning)
{
result = _sw.Elapsed;
}
return result;
}
}
Next, your calling method should use modern conveniences like Task.WhenAll, and a separate method to handle the result as they each complete instead of trying to pull the results out of a list:
private static async Task<string> ProcessAsync(TaskAuditor<string> auditor)
{
try
{
return await auditor.StartAsync();
}
finally
{
Console.WriteLine(auditor.Duration().ToString());
}
}
private static async Task MainAsync()
{
var tas = new List<TaskAuditor<string>>();
tas.Add(new TaskAuditor<string>(() => GetName(string.Empty, 1)));
tas.Add(new TaskAuditor<string>(() => GetName(string.Empty, 2)));
var running = tas.Select(ta => ProcessAsync(ta));
var results = await Task.WhenAll(running);
foreach (var result in results)
{
Console.WriteLine(result);
}
}
This also keeps the code asynchronous up to the point where it can't be asynchronous, namely, Main:
private static void Main()
{
MainAsync().Wait();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}

Parallel Tasks Sharing a Global Variable

Hi I am new to using Parallel tasks. I have a function which I need to run multiple times in parallel. Below is the dummy code to show this,
public MyClass GlobalValue;
static void Main(string[] args)
{
Task task1 = Task.Factory.StartNew(() => SaveValue());
Task task2 = Task.Factory.StartNew(() => SaveValue());
Task task3 = Task.Factory.StartNew(() => SaveValue());
}
public void SaveValue()
{
string val = GetValueFromDB();
if (GlobalValue == NULL)
{
GlobalValue = New MyClass(val);
}
else if (GlobalValue.Key != val)
{
GlobalValue = New MyClass(val);
}
string result = GlobalValue.GetData();
}
Now the line GlobalValue = New GlobalValue(val) is called every time. Kindly help me with this. I think there is a problem with the Global Variable.
You need to synchronize the access to the shared data, as each thread will try to access it at the same time, and see that it's null, then all will allocate.
Note that the synchronization, if done via lock, will likely cause the three threads to effectively run sequentially, as only one thread can enter a lock at a time.
well, why not do
static void Main()
{
var tasks = new[]
{
Task.Factory.StartNew(() => YourFunction()),
Task.Factory.StartNew(() => YourFunction()),
Task.Factory.StartNew(() => YourFunction())
};
Task.WaitAll(tasks)
}
public static string YourFunction()
{
var yourClass = new MyClass(GetValueFromDB());
return yourClass.GetData();
}
I don't see why you need GlobalValue. Is MyClass expensive to instantiate? More notably, you don't do anything with the results so all is moot.
Since the features are available, assuming you're using .Net 4.5 (c# 5.0), you could do
static void Main()
{
await Task.WhenAll(YourFunction(), YourFunction(), YourFunction());
}
public async Task<string> YourFunction()
{
return new MyClass(GetValueFromDB()).GetData();
}
For the sake of illustration, you could still use a global variable but it would massively mitigate the benefits of parallelization. You just have to make sure you serialize access to shared state or use thread safe types that do it for you.
adapted from your example,
private readonly SemaphoreSlim globalLock = new SemaphoreSlim(1));
...
public void SaveValue()
{
string val = GetValueFromDB();
MyClass thisValue;
globalLock.Wait();
try
{
if (this.GlobalValue == NULL)
{
this.GlobalValue = new MyClass(val);
}
else if (this.GlobalValue.Key != val)
{
this.GlobalValue = new MyClass(val);
}
thisValue = this.GlobalValue
}
finally
{
globalLock.Release();
}
string result = thisValue.GetData();
}

Categories

Resources