I would like to replicate something that I have in Java to C#.
I'm NOT looking for, or anything that will involve the driver:
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
I'm using the http://www.awaitility.org/.
Here is the code:
public static void waitForElement(WebElement element) {
with()
.pollDelay(100, TimeUnit.MICROSECONDS)
.and()
.pollInterval(200, TimeUnit.MICROSECONDS)
.await()
.ignoreExceptions()
.until(() -> element.isDisplayed());
}
Thanks
I would do something like
public static async Task WaitForElementAsync(WebElement element)
{
await With(100, 200, true, () => element.isDisplayed());
}
private static async Task With(
int pollDeley,
int pollIntervall,
bool ignoreException,
Func<bool> until)
{
await Task.Delay(pollDeley);
var loop = true;
while (loop)
{
try
{
loop = !until();
if (!loop) break;
await Task.Delay(pollIntervall);
}
catch (Exception ex)
{
if (!ignoreException) throw;
}
}
}
but there might be a better solution if WebElement has an event like IsDisplayedChanged.
Also with this solution you introduce a async call line to your project (which in a web context can be beneficial), to avoid this you can replace the await Task.Delay(...) with Thread.Sleep(...).
Another solution would be to use a timer for the polling
private static async Task With(
int pollDeley,
int pollIntervall,
bool ignoreException,
Func<bool> until)
{
await Task.Delay(pollDeley);
var tcs = new TaskCompletionSource<bool>();
using (var timer = new Timer(pollIntervall))
{
void Poll(object sender, ElapsedEventArgs e)
{
try
{
if (until())
{
if (tcs.TrySetResult(true))
{
timer.Stop();
}
}
}
catch (Exception ex)
{
if (!ignoreException)
{
if (tcs.TrySetException(ex))
{
timer.Stop();
}
}
}
}
timer.Elapsed += Poll;
timer.Start();
await tcs.Task;
timer.Elapsed -= Poll;
}
}
Related
I am trying to get a Windows Service running. The service should use a worker object to spawn multiple tasks.
I use SemaphoreSlim in both the worker object and each task to wait for events to finish, like so:
public static IHostBuilder ConfigureServices(this IHostBuilder builder)
{
builder.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<WorkerService>();
services.AddSingleton<WorkerClient>();
});
return builder;
}
WorkerService
public WorkerService(ILogger<WorkerService> logger, WorkerClient workerClient)
{
_logger = logger;
_workerClient = workerClient;
_bleClient.OnValuesReceived += _bleClient_OnValuesReceived;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await _workerClient.Run();
}
catch(Exception ex)
{
_logger.LogCritical(ex, "Error while running worker client.");
}
await Task.Delay(TimeSpan.FromSeconds(_scanDelay), stoppingToken);
}
}
WorkerClient
public class WorkerClient
{
private Scanner _scanner;
private SemaphoreSlim _lock;
public WorkerClient()
{
_lock = new SemaphoreSlim(0, 1);
_scanner = new Scanner();
_scanner.OnScanFinished += scanner_ScanFinished;
}
public async Task Run()
{
_scanner.Scan();
await _lock.WaitAsync();
}
private void scanner_ScanFinished(object sender, string[] macs)
{
var tasks = new List<Task>();
foreach(var mac in macs)
{
var client = new TaskRunner(mac);
tasks.Add(client.Run());
}
if(tasks.Count > 0)
{
try
{
var task = Task.WhenAll(tasks.ToArray());
await task;
}
catch(Exception ex)
{
_logger.LogError(ex, ex.Message);
}
}
_lock.Release();
}
}
TaskRunner
public class TaskRunner
{
private SemaphoreSlim _lock;
private Client _client;
public TaskRunner(string mac)
{
_lock = new SemaphoreSlim(0, 1);
_client = new Client(mac);
_client.OnWorkFinished += client_WorkFinished;
}
public async Task Run()
{
_client.DoWork();
await _lock.WaitAsync();
}
private void client_WorkFinished(object sender, EventArgs args)
{
_lock.Release();
}
}
This whole construct runs fine when I launch it in a console or inside VS. But it hangs after 1-2 runs when I create a service using sc utility and start it.
I have no idea what I am doing wrong as I am very new to Windows Services and multithreading.
The SemaphoreSlim is probably not an adequate mechanism for converting an event to a Task, because it can't propagate exceptions. The TaskCompletionSource class is a more suitable mechanism for this purpose. Also when subscribing to an event it is a good idea to unsubscribe when we don't want to receive any further notifications. Unsubscribing is achieved using the -= operator.
Here are two extension methods for the classes Scanner and Client, that allow to subscribe to their specific events for a single notification, and propagate this notification as a Task.
public static class ScannerExtensions
{
public static Task<string[]> ScanAsync(this Scanner source)
{
var tcs = new TaskCompletionSource<string[]>();
Action<object, string[]> evenHandler = null;
evenHandler = (s, macs) =>
{
source.OnScanFinished -= evenHandler;
tcs.TrySetResult(macs);
};
source.OnScanFinished += evenHandler;
try
{
source.Scan();
}
catch (Exception ex)
{
source.OnScanFinished -= evenHandler;
tcs.SetException(ex);
}
return tcs.Task;
}
}
public static class ClientExtensions
{
public static Task DoWorkAsync(this Client source)
{
var tcs = new TaskCompletionSource<object>();
EventHandler evenHandler = null;
evenHandler = (s, e) =>
{
source.OnWorkFinished -= evenHandler;
tcs.TrySetResult(null);
};
source.OnWorkFinished += evenHandler;
try
{
source.DoWork();
}
catch (Exception ex)
{
source.OnWorkFinished -= evenHandler;
tcs.SetException(ex);
}
return tcs.Task;
}
}
You could use the extension methods Scanner.ScanAsync and Client.DoWorkAsync to refactor the ExecuteAsync method of your service like this:
private Scanner _scanner = new Scanner();
protected override async Task ExecuteAsync(CancellationToken token)
{
while (true)
{
Task delayTask = Task.Delay(TimeSpan.FromSeconds(_scanDelay), token);
try
{
string[] macs = await _scanner.ScanAsync();
Task[] doWorktasks = macs.Select(mac =>
{
var client = new Client(mac);
return client.DoWorkAsync();
}).ToArray();
await Task.WhenAll(doWorktasks);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
}
await delayTask;
}
}
Not sure if this will solve your problem, but I think that it is a change in the right direction.
If the problem persists you could try creating and awaiting the client.DoWorkAsync tasks one at a time (instead of launching all of them concurrently), to see if that makes any difference.
I realize that OnStart() is just an event so I understand there is no issue with having it return void. However I still get the annoying message giving me a warning
Is there an alternative to allow me to run async methods in OnStart()?
Could I do something like create a Task and have them (or all of my code that's currently in OnStart) run inside that task? or could I use the _ = construct to ignore the output of the Task running inside OnStart?
Update:
Based on Nikosi's suggestion here's what I am thinking to do:
App
public partial class App : Application
{
public async Task CheckLatestVersion()
{
try
{
var isLatest = await CrossLatestVersion.Current.IsUsingLatestVersion();
if (!isLatest)
{
var update = await MainPage.DisplayAlert("New Version", $"\nThere is a new version of this app available.\n\nWould you like to update now?\n", "Yes", "No");
if (update)
{
await CrossLatestVersion.Current.OpenAppInStore();
}
}
}
catch (Exception ex)
{
var ignore = ex;
}
}
private event EventHandler started = delegate { };
protected override void OnStart() {
this.started += onStarted; //Subscribe to event
started(this, EventArgs.Empty); //Raise event
}
protected async void onStarted(object sender, EventArgs args)
{
try
{
if (Connectivity.NetworkAccess == NetworkAccess.Internet)
{
if (Settings.Rev == REV.No && (new[] { 15, 30, 50 }).Contains(Settings.Trk2))
{
await ReviewAppAsync(Settings.Trk2);
}
if (App.devIsPhysical && (new[] { 10, 20, 30 }).Contains(Settings.Trk2))
{
await CheckLatestVersion();
}
// This
await Helper.PopulateMetrics();
// Or this
_ = Helper.PopulateMetrics();
await Helper.LogStart();
}
}
catch(Exception)
{
;
}
this.started -= onStarted; //Unsubscribe (OPTIONAL but advised)
}
}
Helper
public static async Task PopulateMetrics()
{
await Task.Run(() =>
{
if (App.CPUSpeed == 0)
{
var stopWatch = Stopwatch.StartNew();
stopWatch.Start();
ArrayList al = new ArrayList(); for (int i = 0; i < 5000000; i++) al.Add("hello");
App.CPUSpeed = 20000 / stopWatch.ElapsedMilliseconds;
}
});
}
public async Task ReviewAppAsync(int count)
{
try
{
async Task<bool> DelayAndDisplayAlert()
{
await Task.Delay(60000);
return await MainPage.DisplayAlert("Review", $"\nWe noticed that you've used this application {count} times. We'd love to get some feedback for the application.\n\nCan you help us by rating the application or leaving a review?\n", "Yes", "No");
}
if (count == 0 || await DelayAndDisplayAlert())
{
if (Plugin.StoreReview.CrossStoreReview.IsSupported)
{
if (Xamarin.Forms.Device.RuntimePlatform == "iOS")
Plugin.StoreReview.CrossStoreReview.Current.OpenStoreReviewPage("1477984412");
else if (Xamarin.Forms.Device.RuntimePlatform == "Android")
Plugin.StoreReview.CrossStoreReview.Current.OpenStoreReviewPage("com.ankiplus.Japanese");
}
}
}
catch (Exception ex)
{
Helper.RegisterCrash(ex,
new Dictionary<string, string> {
{"ReviewAppAsync", "Exception" },
{"Device Model", DeviceInfo.Model },
{"Exception", ex.ToString()}
});
}
}
OnStart is not actually an event handler. That is a misconception with how the framework was built. You could however create an event and async handler to allow your async code to be awaited as expected.
private event EventHandler started = delegate { };
protected override void OnStart() {
this.started += onStarted; //Subscribe to event
started(this, EventArgs.Empty); //Raise event
}
protected async void onStarted(object sender, EventArgs args) {
try {
//await custom code here
} catch {
//handle errors
}
//this.started -= onStarted; //Unsubscribe (OPTIONAL but advised)
}
asynchronous event handler are the one exception where async void is allowed and can be properly handled.
Using async void with the overridden non event handler OnStart will cause any exceptions encountered to be swallowed as async void on non event handler are fire and forget.
Reference Async/Await - Best Practices in Asynchronous Programming
I am using an asynchronous method. How can I stop its execution when a Timer raises a timeout event?
My code:
public async Task<object> Method()
{
cts = new CancellationTokenSource();
try
{
timer = new System.Timers.Timer(3000);
timer.Start();
timer.Elapsed += (sender, e) =>
{
try
{
timer_Elapsed(sender, e, cts.Token, thread);
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
return;
}
};
await methodAsync(cts.Token);
return "message";
}
catch (OperationCanceledException)
{
return "cancelled";
}
catch (Exception ex)
{
return ex.Message;
}
}
// Async Call
public async Task<object> methodAsync(CancellationToken ct)
{
try
{
pdfDocument = htmlConverter.Convert("path", "");
}
catch(Exception ex)
{
return x.Message;
}
}
// Timer event
void timer_Elapsed(object sender, ElapsedEventArgs e, CancellationToken ct)
{
cts.Cancel();
ct.ThrowIfCancellationRequested();
}
Here's how canceling a task works:
public async Task<object> Method()
{
cts = new CancellationTokenSource();
await methodAsync(cts.Token);
return "message";
}
public Task<object> methodAsync(CancellationToken ct)
{
for (var i = 0; i < 1000000; i++)
{
if (ct.IsCancellationRequested)
{
break;
}
//Do a small part of the overall task based on `i`
}
return result;
}
You have to respond to the change in the property of ct.IsCancellationRequested to know when to cancel the task. There is no safe way for one thread/task to cancel another thread/task.
In your case it appears that you are trying to call a single method that doesn't know about the CancellationToken so you can not cancel this task safely. You must let the thread/task continue to completion.
I think you can try mentioning when to cancel it. Something like
cts.CancelAfter(TimeSpan.FromMilliseconds(5000));
Also, you need to use the cancellation token in the called methods. That's when you will know when to cancel.
I have a method that send some SMS to our customers that look like below:
public void ProccessSmsQueue()
{
SmsDbContext context = new SmsDbContext();
ISmsProvider provider = new ZenviaProvider();
SmsManager manager = new SmsManager(context, provider);
try
{
manager.ProcessQueue();
}
catch (Exception ex)
{
EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);
}
finally
{
context.Dispose();
}
}
protected override void OnStart(string[] args)
{
Task.Factory.StartNew(DoWork).ContinueWith( ??? )
}
So, I have some issues:
I donĀ“t know how long it takes for the method run;
The method can throw exceptions, that I want to write on EventLog
I want to run this method in loop, every 10 min, but only after last execution finish.
How I can achieve this? I thought about using ContinueWith(), but I still have questions on how to build the entire logic.
You should have an async method that accepts a CancellationToken so it knows when to stop, calls ProccessSmsQueue in a try-catch block and uses Task.Delay to asynchronously wait until the next time it needs to run:
public async Task DoWorkAsync(CancellationToken token)
{
while (true)
{
try
{
ProccessSmsQueue();
}
catch (Exception e)
{
// Handle exception
}
await Task.Delay(TimeSpan.FromMinutes(10), token);
}
}
You can call this method when your application starts and Task.Wait the returned task before existing so you know it completes and has no exceptions:
private Task _proccessSmsQueueTask;
private CancellationTokenSource _cancellationTokenSource;
protected override void OnStart(string[] args)
{
_cancellationTokenSource = new CancellationTokenSource();
_proccessSmsQueueTask = Task.Run(() => DoWorkAsync(_cancellationTokenSource.Token));
}
protected override void OnStop()
{
_cancellationTokenSource.Cancel();
try
{
_proccessSmsQueueTask.Wait();
}
catch (Exception e)
{
// handle exeption
}
}
Sample Worker Class that I have used in Windows Services. It supports stopping in a 'clean' way by using a lock.
You just have to add your code in DoWork, set your timer in the StartTimerAndWork method (in milliseconds), and use this class in your service.
public class TempWorker
{
private System.Timers.Timer _timer = new System.Timers.Timer();
private Thread _thread = null;
private object _workerStopRequestedLock = new object();
private bool _workerStopRequested = false;
private object _loopInProgressLock = new object();
private bool _loopInProgress = false;
bool LoopInProgress
{
get
{
bool rez = true;
lock (_loopInProgressLock)
rez = _loopInProgress;
return rez;
}
set
{
lock (_loopInProgressLock)
_loopInProgress = value;
}
}
#region constructors
public TempWorker()
{
}
#endregion
#region public methods
public void StartWorker()
{
lock (_workerStopRequestedLock)
{
this._workerStopRequested = false;
}
_thread = new Thread(new ThreadStart(StartTimerAndWork));
_thread.Start();
}
public void StopWorker()
{
if (this._thread == null)
return;
lock (_workerStopRequestedLock)
this._workerStopRequested = true;
int iter = 0;
while (LoopInProgress)
{
Thread.Sleep(100);
iter++;
if (iter == 60)
{
_thread.Abort();
}
}
//if (!_thread.Join(60000))
// _thread.Abort();
}
#endregion
#region private methods
private void StartTimerAndWork()
{
this._timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
this._timer.Interval = 10000;//milliseconds
this._timer.Enabled = true;
this._timer.Start();
}
#endregion
#region event handlers
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (!LoopInProgress)
{
lock (_workerStopRequestedLock)
{
if (this._workerStopRequested)
{
this._timer.Stop();
return;
}
}
DoWork();
}
}
private void DoWork()
{
try
{
this.LoopInProgress = true;
//DO WORK HERE
}
catch (Exception ex)
{
//LOG EXCEPTION HERE
}
finally
{
this.LoopInProgress = false;
}
}
#endregion
}
I created a static class called "Delay" which basically accepts argument of (double delay, ThreadStart action). Here it is:
public class Delay
{
public static void Execute(double delay, ThreadStart action)
{
Delay.Execute((int)delay, action);
}
public static void Execute(int delay, ThreadStart action)
{
Timer t = new Timer(delay);
t.Elapsed += new ElapsedEventHandler(delegate(object sender, ElapsedEventArgs e)
{
t.Stop();
action();
t.Dispose();
t = null;
});
t.Start();
}
private Timer t;
public Delay(int delay, ThreadStart action)
{
t = new Timer(delay);
t.Elapsed += new ElapsedEventHandler(delegate(object sender, ElapsedEventArgs e)
{
if (t != null)
{
t.Stop();
action();
t.Dispose();
}
t = null;
});
}
public void Execute()
{
t.Start();
}
public void Cancel()
{
if (t != null)
{
t.Stop();
t.Dispose();
}
t = null;
}
}
I have a few questions.
Is it really bad? My friend told me it'd give me errors for sure, but I haven't experienced any.
Is there a way to improve it? It works fine now, but I don't want it to leak memory in the future.
Best regards.
You don't need any of this.Simply you can use async/await feature like:
public async Task Execute(double delay, ThreadStart action)
{
await Task.Delay((int)delay).ContinueWith((x) => action());
}