I was creating a puzzle with a bit of information in different sources to create this...
System.Threading.Thread th;
th = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
InvokeOnMainThread(() =>
{
lbMemFree.Text = "memory free: " + NSProcessInfo.ProcessInfo.PhysicalMemory; // this works!
});
}));
th.Start();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
th.Sleep(500); // delay execution for 500 ms
// more code
});
The idea is to create something that update the label times in time. In this scenario: 500ms.
But the th.Sleep(500) don't allow the app to compile. It's says: Error CS0176: Static member System.Threading.Thread.Sleep(int) cannot be accessed with an instance reference, qualify it with a type name instead (CS0176).
You can use async await for this.
Interval
public class Interval
{
public static async Task SetIntervalAsync(Action action, int delay, CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
await Task.Delay(delay, token);
action();
}
}
catch(TaskCanceledException) { }
}
}
usage (e.g. Console Application for demo)
class Program
{
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
Interval.SetIntervalAsync(DoSomething, 1000, cts.Token);
Console.ReadKey(); // cancel after first key press.
cts.Cancel();
Console.ReadKey();
}
public static void DoSomething()
{
Console.WriteLine("Hello World");
}
}
Use the CancellationTokenSource to cancel the execution of the interval.
Related
I want to add ability to cancel long running external method (in code below is it LongOperation method from SomeExternalClass class), so I wrap this method into task and add custom task extension for handle timeout.
TimeoutException is successful throwed after specific period of time, but external method is not canceled. How I can cancel LongOperation?
class Program
{
private static CancellationTokenSource cancellationTokenSource;
static async Task Main(string[] args)
{
cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Token.ThrowIfCancellationRequested();
try
{
await SearchTask().WithTimeout(TimeSpan.FromSeconds(2));
}
catch (TimeoutException)
{
Console.WriteLine("Timeout");
cancellationTokenSource.Cancel();
}
Console.WriteLine("Program end");
Console.ReadKey();
}
private static Task SearchTask()
{
return Task.Run(() =>
{
Console.WriteLine("Start task");
SomeExternalClass.LongOperation();
Console.WriteLine("End task");
}, cancellationTokenSource.Token);
}
}
public static class SomeExternalClass
{
// this is simulation of long running 3rd party method
public static void LongOperation()
{
Console.WriteLine("Start LongOperation");
Thread.Sleep(10000);
Console.WriteLine("End LongOperation");
}
}
public static class TaskExtension
{
public static async Task WithTimeout(this Task task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
await task;
}
throw new TimeoutException();
}
}
The output is:
Start task
Start LongOperation
Timeout
Program end
End LongOperation
End task
I have a console app that instantiates a WeatherClientManager class.
The main thread in the console app requests current weather status in the WeatherClientManager class, but the WeatherClientManager class continuously receives data from a server.
In code:
public static void Main(string [])
{
Program p = new Program();
Task.Run(()=>p.RunLoop());
}
class Program{
WeatherClientManager wcM;
public void RunLoop()
{
wcM = new WeatherClientManager ();
await wcM.InitiateConnection().ConfigureAwait(false);
}
}
class WeatherClientManager
{
public async Task<bool> InitiateConnection()
{
TCPClient tcpClient = new TcpClient(GetTCPDetailsFromConfig())
await tcpClient.ConnectAsync()
CancellationTokenSource cts = new CancellationTokenSource();
if(tcpClient.Connected)
{
Task.Run(()=>ReceiveTask(cts.Token));
Task.Run(()=>SendKeepAlive(cts.Token));
return true;
}
return false;
}
private void ReceiveTask(CancellationToken t)
{
try{
networkStream.Receive(..) // throws exception
}
catch(Exception e)
{
Stop(e);
}
}
private void SendKeepAlive(CancellationToken t)
{
while(!t.IsCancellationRequested)
{
try{
networkStream.Write(..) // throws exception
}
catch(Exception e)
{
Stop(e);
}
}
}
private void Stop(Exception e )
{
log.Error(e);
e.Cancel();
}
}
One of many crap ideas I have is:
Task.Run( () =>
{
while(true)
{
var t1 = Task.Run(()=>ReceiveTask(cts.Token));
var t2= Task.Run(()=>SendKeepAlive(cts.Token));
try{
Tasks.WhenAny(); // should block
}
catch(Exception e)
{
}
finally{
Cleanup();
InitiateConnections();
}
}
}
But I hate the idea of spinning a task to control two sub tasks. My problem is where and how to re-initiate the connection. Any ideas?
EDIT:
I've updated the code such that WeatherClientManager has a OnDisconnectDetected event. So the Program.cs class subscribes like so:
weatherServerManager.OnDisconnectDetected += HandleDisconnectDetection
public async void HandleDisconnectDetection()
{
wsM = new WeatherClientManager ();
wsM.InitiateConnection().ConfigureAwait(false);
}
private void SendKeepAlive(CancellationToken t)
{
while (...)
{
try{}
catch(Exception e)
{
OnDisconnectDetected?.Invoke();
}
}
}
When the handler is invoked by the WeatherClientManager it creates a new task that should continue in a different context. The KeepAlive task should exit then.
Still feels hacky but ideas welcome!
As a general rule, I prefer composition of methods over raising events. In particular, avoid the Task.Run-based fire-and-forget.
In the case of asynchronous sockets, I think it makes sense to give each socket a main loop:
class WeatherClientManager
{
public async Task MainLoop()
{
TCPClient tcpClient = new TcpClient(GetTCPDetailsFromConfig())
await tcpClient.ConnectAsync();
CancellationTokenSource cts = new CancellationTokenSource();
var receiveTask = Task.Run(()=>ReceiveTask(cts.Token));
var keepaliveTask = Task.Run(()=>SendKeepAlive(cts.Token));
await Task.WhenAll(receiveTask, keepaliveTask);
}
}
These can then be composed into the main program's main loop:
class Program
{
public async Task RunLoop()
{
while (true)
{
wcM = new WeatherClientManager();
await wcM.MainLoop();
}
}
}
which in turn is composed into Main:
public static void Main(string [])
{
Program p = new Program();
p.RunLoop().GetAwaiter().GetResult();
}
By avoiding fire-and-forget, you're ensuring that your code will always observe all exceptions. Ignoring tasks is occasionally okay but usually a mistake.
class Program
{
static void Main(string[] args)
{
Class1 testcla = new Class1();
testcla.test();
}
}
class Class1
{
public void test()
{
test1();
}
public async void test1()
{
await test2();
}
public async Task<string> test2()
{
WebClient testwc = new WebClient();
var content = await testwc.DownloadStringTaskAsync("www.yahoo.com");
return content;
}
}
Hi,
i need wait the DownloadStringTaskAsync finished download the content, however when it execute to the line, it exit without any error.
Kindly advice
Try below code. As mentioned by #Dispersia, the url should contain http, https, ftp, etc.
You code has to wait for the async task to finish. For a starter, below description will help you to understand async.
Look at main() method, it just runs a task and that's all. The main() do not depend on any of the value of the result of async task, it ends and application is closed. Async task i.e. DownloadStringTaskAsync() takes much time to complete but by the time result is received from the WebClient, main method finishes it execution and application closes. Hence you will never see any result.
I have made a change to the main function to wait until enter is pressed. Wait for the DownloadStringTaskAsync to finish you will see content length in the output window. Then you can press enter key
static void Main(string[] args)
{
Class1 testcla = new Class1();
testcla.test();
Console.WriteLine("Wait for async task to finish.");
Console.ReadLine();
}
class Class1
{
public void test()
{
test1();
}
public async void test1()
{
await test2();
}
public async Task<string> test2()
{
try
{
WebClient testwc = new WebClient();
var content = await testwc.DownloadStringTaskAsync("http://www.yahoo.com");
Console.WriteLine("Length of content received: " + content.Length.ToString());
return content;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
finally
{
Console.WriteLine("Async task done. Press enter to exit.");
}
}
}
I am working on xamarin forms PCL + iOS. I want to cancel a task when it enters background. And start it all over when app enters foreground.
This is what I have tried so far.. I am not sure if my way cancels any task or what is it that is happening here?
async void getData()
{
bool isSuccess = await getSomeData();
if(isSuccess)
await getSomeMoreData();
}
CancellationTokenSource cts;
async Task<bool> getSomeData()
{
cts = new CancellationTokenSource();
AppEntersBackgorund += (sender,args) => { cts. cancel();});
CancellationToken token = new CancellationToken();
token = cts.token;
await Task.Run(() => {
token.ThrowIfCancellationRequested();
isSuccess = ParserData(token); // parsedata also checks periodically if task is cancelled
},token); //what happens here when cancel called?
return isSuccess;
}
async void getSomeMoreData()
{
if(!cts.IsCancellationRequested)
cts = new CancellationTokenSource();
AppEntersBackgorund += (sender,args) => { cts. cancel();});
CancellationToken token = new CancellationToken();
token = cts.token;
await Task.Run(() =>
{
token.ThrowIfCancellationRequested();
ParseSomeMoreData(token);
},token);
}
When app enters foregorund, I again call the getData() method so that i start all over again.
What happens is that, Task is not getting cancelled, rather getSomeMoreData is getting called twice ( or the no. of times the app goes from background to foreground) .
Can someone explain how I can achieve this? And what is happening here?
Actually, this is not a Xamarin problem, it is just a C# problem except the app's enter foreground/background events.
For the requirements you need, you should make a task manager object to implement it.
I wrote a sample code for you:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
namespace BackGroundTask
{
public class TaskManager
{
//The instance property
private static TaskManager instance;
public static TaskManager Instance{
get{
if(null == instance)
instance = new TaskManager();
return instance;
}
}
private bool actionTaskFreeFlag = true;//Flag for if actionTask is available or not
private Queue<Action> taskQueue;//A queue to collect the tasks you added into the manager
private Task scanTask;//A Task to sacn the queue
private Task actionTask;//A task to do the current action
private Thread actionTaskRunningThread;//Record the thread that current action is working on
public TaskManager()
{
taskQueue = new Queue<Action>();
scanTask = new Task(() =>
{
while (true)
{
if (actionTaskFreeFlag && taskQueue.Count > 0)//If there still something to do and the actionTask is available then do the action
{
actionTaskFreeFlag = false;
Action action = taskQueue.Dequeue();
actionTask = new Task(() => {
actionTaskRunningThread = System.Threading.Thread.CurrentThread;
action();
});
actionTask.Start();
actionTask.ContinueWith(delegate {
actionTaskFreeFlag = true;
});
}
}
});
scanTask.Start();
}
public void AddAction(Action action)
{
taskQueue.Enqueue(action);
}
public void CancelCurrentTaskAndClearTaskQueue()
{
Console.WriteLine("CancelCurrentTaskAndClearTaskQueue");
if(null != actionTaskRunningThread)
actionTaskRunningThread.Abort();
taskQueue.Clear();
}
}
}
And this is a sample code for how to use it to do the stuff you want:
//App enter background event
AppDelegate.Instance.AppDidEnterBackground += delegate {
TaskManager.Instance.CancelCurrentTaskAndClearTaskQueue();
};
//App enter forcenground event
AppDelegate.Instance.AppWillEnterForeground += delegate {
if (AppDelegate.FlagForGetData)
{
TaskManager.Instance.AddAction(GetData);
TaskManager.Instance.AddAction(GetMoreData);
}
};
And this is the methods for testing:
private void GetData()
{
AppDelegate.FlagForGetData = true;
Console.WriteLine("Began getting data.");
System.Threading.Thread.Sleep(5000);
AppDelegate.FlagForGetData = false;
Console.WriteLine("Getting data succeed.");
}
private void GetMoreData()
{
Console.WriteLine("Began getting more data.");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Getting more data succeed.");
}
Hope it can help you.
I am currently trying to implement a substitute for .Net 4.5's Task.Delay() method in a program that must target .Net 4.0. I found the following code at this blog.
/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource.
A TaskCompletionSource gives you a 'slave' Task that you can manually signal.
Calling SetResult() signals the task as complete, and any continuations kick off. */
void Main()
{
for (int i = 0; i < 10000; i++)
{
Task task = Delay (2000);
task.ContinueWith (_ => "Done".Dump());
}
}
Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
Tasks are fairly new to me. System.Threading.Timer and TaskCompletionSource are brand new to me (as of today), and I'm struggling a bit with them. All that aside, I'm wondering how I might add CancellationToken functionality to this code. I'm assuming I could add a parameter to the Delay() method like this:
Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
... but then, where do I put the logic for checking the token and getting out of the method? Somewhere in the callback? Is this even possible?
I've tried to change your code as little as possible but here is a working example that behaves in the same way as Task.Delay.
It's important to note that I use TrySetCanceled and TrySetResult because the Timer could finish after the task is canceled. Ideally you want to stop the timer.
Also note a canceled task will throw a TaskCanceledException
static void Main(string[] args)
{
// A cancellation source that will cancel itself after 1 second
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
// This will only wait 1 second because as it will be cancelled.
Task t = Delay(5000, cancellationTokenSource.Token);
t.Wait();
Console.WriteLine("The task completed");
}
catch (AggregateException exception)
{
// Expecting a TaskCanceledException
foreach (Exception ex in exception.InnerExceptions)
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Done");
Console.ReadLine();
}
private static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
token.Register(() => tcs.TrySetCanceled());
Timer timer = new Timer(_ => tcs.TrySetResult(null));
timer.Change(milliseconds, -1);
return tcs.Task;
}
Reading a bit more into your question. If you need Task.Delay and you're targeting .NET 4.0 then you should use the Microsoft Async nuget package from http://www.nuget.org/packages/Microsoft.Bcl.Async/ it contains the method TaskEx.Delay
Like this:
token.Register(() => tcs.TrySetCancelled());
Here you are a version that prevents disposal of timer by the garbage collector
public static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
var timer = new OneShotTimer((t) => {
using ((OneShotTimer)t)
tcs.SetResult(null);
});
token.Register(() => {
if (timer.TryCancel())
{
using (timer)
tcs.SetCanceled();
}
});
timer.Start(milliseconds);
return tcs.Task;
}
public class OneShotTimer : IDisposable
{
private readonly object sync = new object();
private readonly TimerCallback oneShotCallback;
private readonly Timer timer;
private bool isActive;
public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite)
{
this.oneShotCallback = oneShotCallback;
this.isActive = dueTime != Timeout.Infinite;
this.timer = new Timer(callback, this, dueTime, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public void Start(int dueTime)
{
if (!tryChange(true, dueTime))
throw new InvalidOperationException("The timer has already been started");
}
public bool TryCancel()
{
return tryChange(false, Timeout.Infinite);
}
public bool tryChange(bool targetIsActive, int dueTime)
{
bool result = false;
lock (sync)
{
if (isActive != targetIsActive)
{
result = true;
isActive = targetIsActive;
timer.Change(dueTime, Timeout.Infinite);
}
}
return result;
}
private static void callback(object state)
{
var oneShotTimer = (OneShotTimer)state;
if (oneShotTimer.TryCancel())
oneShotTimer.oneShotCallback(oneShotTimer);
}
}