I have a line of code in an app that looks like this:
string botMessage = chatterBotSession.Think(input);
It's querying a chat bot service and stores the response in botMessage. Sometimes however, the chat bot can take too long to think of a response.
Is there a way I can run the code like normal, but if it doesn't complete in one second cancel it and then run some handler that can alert the user that the service took too long?
Like, normally I might do this:
string botMessage = chatterBotSession.Think(input);
Console.WriteLine("The bot responded with: " + botMessage);
but if the bot is slow the second line doesn't get executed (fast enough). How can I limit the bot's "thinking" time to one second, and run the rest of the code (that would normally run) as soon as it completes if it was successful or run a separate bit of code to display an error message if it hasn't completed.
You can wrap the bot's service call in a Task.Run call and wait for it a predefined amount of time. It would look something like this
static void Main(string[] args)
{
var task = Task.Run(() => chatterBotSession.Think(input));
if (task.Wait(1000))
{
Console.WriteLine(task.Result);
}
else
{
Console.WriteLine("Couldn't get an answer in a timely manner");
}
Console.ReadLine();
}
It is very easy to limit the task execution by using CancellationTokenSource with timeout:
var cancellationToken = new CancellationTokenSource();
var task = chatterBotSession.Think(cancellationToken.Token);
cancellationToken.CancelAfter(TimeSpan.FromMilliseconds(1000)); // cancel after 1sec
await task;
In Think method you should add call to CancellationToken.ThrowIfCancellationRequested
Calling Bot:
static void Main(string[] args)
{
TalkingBot bot = new TalkingBot();
try
{
Console.WriteLine("Thinking started...");
Console.WriteLine(bot.Think("Some input...", 2000));
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}", ex.Message);
}
Console.ReadLine();
}
And Bot itself:
class TalkingBot
{
string input = null;
int timeout = 0;
string asyncThnikResult = null;
public string Think(string input, int timeout)
{
DateTime timeLimit = DateTime.Now.AddMilliseconds(timeout);
this.input = input;
this.timeout = timeout;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(AsyncThnik));
thread.Start();
//wait for result, in this case
while (string.IsNullOrEmpty(asyncThnikResult))
{
if (timeLimit <= DateTime.Now)
{
throw new Exception("Timeout occured!");
}
System.Threading.Thread.Sleep(10);
}
//return result...
return this.asyncThnikResult;
}
/// <summary>
/// Do your thing async...
/// </summary>
void AsyncThnik()
{
string temp = "This value will never be returned due to timeout limit...";
System.Threading.Thread.Sleep(timeout + 1000); //add second to produce timeout...
this.asyncThnikResult = temp;
}
}
Related
I`m working on implementing a get method for cache. This method will return to caller if a maximum wait time has passed(in my case 100ms for tests).
My issue is that the exception NEVER reaches the catch, after the timer triggered the event.
Please help me understand why? (I read that events are executed on the same thread, so that should`t be the issue)
public static T Get<T>(string key, int? maxMilisecondsForResponse = null)
{
var result = default(T);
try
{
// Return default if time expired
if (maxMilisecondsForResponse.HasValue)
{
var timer = new System.Timers.Timer(maxMilisecondsForResponse.Value);
timer.Elapsed += OnTimerElapsed;
timer.AutoReset = false;
timer.Enabled = true; // start the timer
}
var externalCache = new CacheServiceClient(BindingName);
Thread.Sleep(3000); // just for testing
}
catch (Exception ex)
{
// why is the exception not caught here?
}
return result;
}
private static void OnTimerElapsed(object source, System.Timers.ElapsedEventArgs e)
{
throw new Exception("Timer elapsed");
}
The timer fires on it's own thread. You can read more about it in this answer.
The answer to your question is to use async methods that can be cancelled. Then you can use a cancellation token source and do it the proper way instead of homebrewing a solution with timers.
You can find a good overview here.
For example:
cts = new CancellationTokenSource();
cts.CancelAfter(2500);
await Task.Delay(10000, cts.Token);
This would cancel the waiting task after 2500 (of 10000) because it took too long. Obviously you need to insert your own logic in a task instead of just waiting.
From MSDN
The Timer component catches and suppresses all exceptions thrown by
event handlers for the Elapsed event. This behavior is subject to
change in future releases of the .NET Framework.
And continues
Note, however, that this is not true of event handlers that execute
asynchronously and include the await operator (in C#) or the Await
operator (in Visual Basic). Exceptions thrown in these event handlers
are propagated back to the calling thread.
Please take a look Exception Handling (Task Parallel Library)
An applied example below:
public class Program
{
static void Main()
{
Console.WriteLine("Begin");
Get<string>("key", 1000);
Console.WriteLine("End");
}
public static T Get<T>(string key, int? maxMilisecondsForResponse = null)
{
var result = default(T);
try
{
var task = Task.Run(async () =>
{
await Task.Delay(maxMilisecondsForResponse.Value);
throw new Exception("Timer elapsed");
});
task.Wait();
}
catch (Exception ex)
{
// why the exception is not catched here?
Console.WriteLine(ex);
}
return result;
}
}
The timer is being executed in the own thread but you can't catch the exception at the caller level. So, it is not a good approach to use timer in this case and you can change it by creating the Task operation.
var result = default(T);
CacheServiceClient externalCache;
if (!Task.Run(() =>
{
externalCache = new CacheServiceClient(BindingName);
return externalCache;
}).Wait(100))//Wait for the 100 ms to complete operation.
{
throw new Exception("Task is not completed !");
}
// Do something
return result;
I'm working on a C# project, want to make a small pause about 2 seconds inside a procedure.
Actually I have tried to use Invoke, but as you know, we can't use it inside a class this kind of procedure.
Here is my code for more details:
public class GenerateFile
{
public CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
string script = string.Format(" DECLARE #RC INT " +
" DECLARE #Daftar_No INT = '{0}' " +
" DECLARE #hokm_type_code INT = 100 " +
" DECLARE #Channelno INT = '{1}' " +
" DECLARE #Id_No BIGINT = '{2}' " +
" EXEC #rc = [dbo].[Hokm_with_type] #Daftar_No, #hokm_type_code, #Channelno, #Id_No ",
Daftar_No,
Channelno,
NationalCode);
try
{
IEnumerable<string> commandStrings = Regex.Split(script, #"^\s*GO\s*$",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
Connect();
foreach (string commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using (var command = new SqlCommand(commandString, Connection))
{
command.ExecuteNonQuery();
}
}
}
DisConnect();
string FaxFilePath = InternalConstant.FaxFilePath + "\\" + string.Format("Lhokm{0}.tif", Channelno);
// I want to make a pause in here without locking UI
if (File.Exists(FaxFilePath))
return CSPFEnumration.ProcedureResult.Success;
else
return CSPFEnumration.ProcedureResult.Error;
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
return CSPFEnumration.ProcedureResult.Error;
}
}
}
I have tried await too, but I cant return a proper value. because in this procedure if I use await, the value will return before finishing await.
Edit:
And also I dont want to use Thread.Sleep because it will lock UI.
Thanks for any helping.
Use async await feature :
Mark your method as async .
Add Task.Delay(2000) as the waited task.
public async CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
-----
// I want to make a pause in here without locking UI
await Task.Delay(2000);
-----
}
Asking for downvotes:
DoEvents
Warning: Total, Complete and Inexcusably Flagrant Barnyard Programming:
// before call (disable the UI element that called this so it can't re-enter)
DateTime st = DateTime.Now();
while(DateTime.Now.Subtract(st).TotalSeconds<3)
System.Windows.Forms.DoEvents();
// after call (re-enable UI element)
This will appear to work. No responsibility if people point and laugh.
Hey, you asked!
You can look around Task.Delay() it will not block current thread and continue execution after number of milliseconds.
Exmaple usage from msdn:
Stopwatch sw = Stopwatch.StartNew();
var delay = Task.Delay(1000).ContinueWith(_ =>
{ sw.Stop();
return sw.ElapsedMilliseconds; } );
Console.WriteLine("Elapsed milliseconds: {0}", delay.Result);
// The example displays output like the following:
// Elapsed milliseconds: 1013
Or maybe look around Timer class.
I can see it working with events or Tasks (if you cannot use async / await). This is how to create event. We can use separate Thread to check if file is created and fire event if it is:
public class FileGenEventArgs : EventArgs
{
public string ProcedureResult { get; set; }
}
public class GenerateFile
{
public event EventHandler<FileGenEventArgs > fileCreated;
public GenerateFile()
{
// subscribe for this event somewhere in your code.
fileCreated += GenerateFile_fileCreated;
}
void GenerateFile_fileCreated(object sender, FileGenEventArgs args)
{
// .. do something with args.ProcedureResult
}
private void FileCheck()
{
Thread.Sleep(2000); // delay
fileCreated(this, new FileGenEventArgs()
{
ProcedureResult = File.Exists(FaxFilePath) ?
CSPFEnumration.ProcedureResult.Success :
CSPFEnumration.ProcedureResult.Error
});
}
public void GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
try
{
// this .Sleep() represents your sql operation so change it
Thread.Sleep(1000);
new Thread(FileCheck).Start();
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
}
}
}
Pros :
Pause that you wanted
Doesn't block the UI thread.
Event-based approach (which is proper way of dealing with this kind of problems)
Cons :
Requires to refactor your code
The most easy thing to wait while keeping the UI responsive is using async-await.
To do this, you must declare your function async, and return Task instead of void and Task<TResult> instead of TResult:
public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
string Daftar_No,
string Channelno,
string NationalCode)
{
// do your stuff,
}
Now whenever you do something that takes some time, use the async version of the function to start the process. While this process is running, you can do other stuff. When you need the result await for the task, and you get the void if the async returns Task, or the TResult if the async returns Task<TResult>
public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
string Daftar_No,
string Channelno,
string NationalCode)
{
IEnumerable<string> commandStrings = Regex.Split(
script, #"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
Connect();
foreach (var commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using (var command = new SqlCommand(commandString, Connection))
{
Task<int> task = command.ExecuteNonQueryAsync();
// while the command is being executed
// you can do other things.
// when you need the result: await
int result = await task;
// if useful: interpret result;
}
}
}
DisConnect();
... etc.
}
Every function that calls an async function should be declared async
every async function returns Task instead of void and Task<TResult> instead of TResult
There is only one exception: the event handler may return void.
Example of async event handler:
private async void OnButton1_Clicked(object sender, ...)
{
var task = GenerateFaxFile(...);
// while the fax file is generated do some other stuff
// when you need the result:
var procedureResult = await task;
Process(procedureResult);
}
Note that everything is processed by the UI thread. The only difference is that as soon as anything time consuming happens, the process doesn't have a busy wait, but processes UI input.
The above is enough to keep your UI responsive. You said you wanted to know how to wait some time. From the rest of your question I understand that you meant: how to interrupt the procedure while it is waiting for something, so the UI can do other thing. If you really need to wait some time while keeping the UI responsive, use Task.Delay(TimeSpan).
Eric Lippert (thanx Eric!) explained async-await as follows in Stackoverflow - async/await - Is this understanding correct?
Suppose for breakfast you have to toast bread and cook eggs. There are several scenarios for it:
Start toasting bread. Wait until it is finished. Start cooking eggs, wait until it is finished. Synchronous processing. While you are waiting for the bread to toast you can't do anything else.
Start toasting bread, while the bread is being toasted start cooking eggs. when the eggs are cooked wait until the bread finished toasting. This is called Asynchronous, but not concurrent. It is done by the main thread and as long as this thread does something, the main thread can't do anything else. But while it is waiting it has time to do other things (make some tea for instance)
Hire cooks to toast the bread and cook the eggs. Wait until both are finished. Asynchronous and concurrent: the work is done by different threads. This is the most expensive because you have to start new threads.
Finally a note about your exception handling
Do you notice that if an exception occurs you don't disconnect?. The proper way to make sure that disconnect is always called is the following:
try
{
Connect();
... do other stuff
}
catch (Exception exc)
{
... process exception
}
finally
{
Disconnect();
}
The finally part is always executed, regardless of any exception being thrown or not.
You can use simple Thread Pool to archive this. However your return has to do asynchronously so it doesn't lockup the gui.
public void GenerateFaxFile(string Daftar_No, string Channelno,
string NationalCode, Action<CSPFEnumration.ProcedureResult> result)
{
ThreadPool.QueueUserWorkItem(o =>
{
string script = "your script";
try
{
// more of your script
// I want to make a pause in here without locking UI
while (true)
{
// do your check here to unpause
if (stopMe == true)
{
break;
}
Thread.Sleep(500);
}
if (File.Exists(FaxFilePath))
{
result(CSPFEnumration.ProcedureResult.Success);
return;
}
else
{
result(CSPFEnumration.ProcedureResult.Error);
}
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
result(CSPFEnumration.ProcedureResult.Error);
return;
}
});
}
public void HowToUseMe()
{
GenerateFaxFile("", "", "", result => {
if (result == CSPFEnumration.ProcedureResult.Error)
{
// no good
}
else
{
// bonus time
}
});
}
You should use the old good background thread (see answer written by FabJan) or you can use async and await with synchronization context:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void buttonStart_Click(object sender, EventArgs e)
{
await progressBar1.DoProgress(2000);
Trace.WriteLine("Done");
MessageBox.Show("Done");
}
private void buttonMoveButton1_Click(object sender, EventArgs e)
{
//to prove UI click several times buttonMove while the task is ruunning
buttonStart.Top += 10;
}
}
public static class WaitExtensions
{
public static async Task DoProgress(this ProgressBar progressBar, int sleepTimeMiliseconds)
{
int sleepInterval = 50;
int progressSteps = sleepTimeMiliseconds / sleepInterval; //every 50ms feedback
progressBar.Maximum = progressSteps;
SynchronizationContext synchronizationContext = SynchronizationContext.Current;
await Task.Run(() =>
{
synchronizationContext.OperationStarted();
for (int i = 0; i <= progressSteps; i++)
{
Thread.Sleep(sleepInterval);
synchronizationContext.Post(new SendOrPostCallback(o =>
{
Trace.WriteLine((int)o + "%");
progressBar.Value = (int)o;
}), i);
}
synchronizationContext.OperationCompleted();
});
}
}
It could appear that MessageBox done Shows before the ProgressBar is on its Maximum. I blame for this magic animation of progressBar in Windows 8. Please correct me if I am wrong.
Is there any way to time out a method after some time if it does not return result without using asynchronous programming?
If it cant be done without asynchronous programming ,Please give me the asynchronous solution but the former is preferred.
static void Main(string[] args){
string s=function(string filename); //want to time this out in 10 secs if does not return result
}
public string function(string filename){
//code placed here to ftp a file and return as string
//i know .net ftp library has its own timeouts, but i am not sure if they are that trust worthy
}
You can do it in this way. Taken from here How to set timeout for a line of c# code
private static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
int timeOut = 10000; // 10 s
string output = ""; // the return of the function will be stored here
var task = Task.Factory.StartNew(() => output = function(), token);
if (!task.Wait(timeOut, token))
Console.WriteLine("The Task timed out!");
Console.WriteLine("Done" + output);
}
private static string function()
{
Task.Delay(20000).Wait(); // assume function takes 20 s
return "12345";
}
Obviously this will not print 12345. because the method timesout.
I'd imagine you could just set a limit for the amount of times that the loop takes place for. I'll admit, I wouldn't program like this, but I also wouldn't have something like this not be asynchronous so don't judge.
int loopnumber = 0;
int loopmax = 1000;
while (loopnumber <= 1000)
{
//Do whatever
loopnumber++;
}
I am currently working with a Serial Port, and the API I use will some times hang on a read, even when its own time out is set.
This is not a big problem, but i need to do some work when that happens and the hanging thread needs to be shutdown. I have tried that with the following, but it has been giving me problems as the API call is not terminated, but allowed to continue while the rest of the code continues, and the TimeoutException was thrown. How can i use Tasks to be able to cancel a hanging task after a certain amount of time?
CancellationToken token = new CancellationToken();
var task = Task.Factory.StartNew(() =>
{
CallingAPIThatMightHang(); // Example
}, token);
if (!task.Wait(this.TimeToTimeOut, token))
{
throw new TimeoutException("The operation timed out");
}
CancellationToken is of the form of cooperative cancellation. You need to moderate the token while executing your operation and watch if a cancelation has been requested.
From your code block, it seems as you have one long running synchronous operation which you offload to a threadpool thread. If that's the case, see if you can separate that serial call to chunks, where you can poll the token after read chunk. If you can't, cancellation wont be possible.
Note that in order to request cancellation, you'll have to create a CancellationTokenSource, which you'll later be able to call it's Cancel() method.
As a side note, serial port is async IO, You can use naturally async API's instead of offloading a synchronous to a threadpool thread.
Edit:
#HansPassant gave a better idea. Run the third party call inside another process, one which you keep a reference to. Once you need to terminate it, kill the process.
For example:
void Main()
{
SomeMethodThatDoesStuff();
}
void SomeMethodThatDoesStuff()
{
// Do actual stuff
}
And then launch it in a separate process:
private Process processThatDoesStuff;
void Main()
{
processThatDoesStuff = Process.Start(#"SomeLocation");
// Do your checks here.
if (someCondition == null)
{
processThatDoesStuff.Kill();
}
}
If you need to communicate any result between these two processes, you can do those via several mechanisms. One would be writing and reading the Standard Output of the process.
I am sadly not able to use any other framework, and i am not able to just change the API i am calling so it can use a Cancellation Token.
This is how i chose to solve the problem.
class Program
{
static void Main(string[] args)
{
try
{
var result = TestThreadTimeOut();
Console.WriteLine("Result: " + result);
}
catch (TimeoutException exp)
{
Console.WriteLine("Time out");
}
catch (Exception exp)
{
Console.WriteLine("Other error! " + exp.Message);
}
Console.WriteLine("Done!");
Console.ReadLine();
}
public static string TestThreadTimeOut()
{
string result = null;
Thread t = new Thread(() =>
{
while (true)
{
Console.WriteLine("Blah Blah Blah");
}
});
t.Start();
DateTime end = DateTime.Now + new TimeSpan(0, 0, 0, 0, 1500);
while (DateTime.Now <= end)
{
if (result != null)
{
break;
}
Thread.Sleep(50);
}
if (result == null)
{
try
{
t.Abort();
}
catch (ThreadAbortException)
{
// Fine
}
throw new TimeoutException();
}
return result;
}
}
I'm creating a console app that would:
Call a method to check an email account(I've done this step)
Convert the attachemnt to pdf(I've done this step)
Then once the conversion is complete wait 30 seconds
Repeat the previous 3 steps continously
I've done steps 1) and 2) in the ProcessMailMessages() method.
The following code works but I want to know if I am on the right track or is there a better way to poll a email client?
private static int secondsToWait = 30 * 1000;
static void Main(string[] args)
{
bool run = true;
do
{
try
{
Task theTask = ProcessEmailTaskAsync();
theTask.Wait();
}
catch (Exception e)
{
Debug.WriteLine("<p>Error in Client</p> <p>Exception</p> <p>" + e.Message + "</p><p>" + e.StackTrace + "</p> ");
}
GC.Collect();
} while (run);
}
static async Task ProcessEmailTaskAsync()
{
var result = await EmailTaskAsync();
}
static async Task<int> EmailTaskAsync()
{
await ProcessMailMessages();
await Task.Delay(secondsToWait);
return 1;
}
static async Task ProcessMailMessages()
{
...............................................................................
}
Instead of looping in main, you could use a timer. In main, you would set up the timer and then you could just wait on a Console.Readline() to keep the console from closing.
Edit -- here's an example
using System;
namespace ConsoleApplication1
{
class Program
{
private const int MilliSecondsToWait = 30000;
private static System.Timers.Timer EmailTimer;
static void Main(string[] args)
{
EmailTimer = new System.Timers.Timer(MilliSecondsToWait);
EmailTimer.Elapsed += EmailTimer_Elapsed;
EmailTimer.Start();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
// if you hit enter, the app will exit. It is possible for the user to exit the app while a mail download is occurring.
// I'll leave it to you to add some flags to control that situation (just trying to keep the example simple)
}
private static void EmailTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// stop the timer to prevent overlapping email downloads if the current download takes longer than MilliSecondsToWait
EmailTimer.Stop();
try
{
Console.WriteLine("Email Download in progress.");
// get your email.
}
catch (System.Exception ex)
{
// handle any errors -- if you let an exception rise beyond this point, the app will be terminated.
}
finally
{
// start the next poll
EmailTimer.Start();
}
}
}
}
your code works well, its avoiding the use of timers! and also making your code asynchronous with the async/await (TPL). You are on the right track!
This is a gratuitous use of async/await since it is a console app and you are just blocking until the call completes anyway. Might as well just call ProcessMailMessages() from inside your do/while loop and be done with it.