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());
}
Related
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;
}
}
Here in the below code I want to stop the thread which is created in StartInvokeExplorer function. Also the starter function in the StartInvokeExplorer is a keyhook function.
public void InvokeExplorerStart_Click(object sender, RoutedEventArgs e)
{
Automate.IsInvokeExplorerClicked = true;
if (InvokeExplorer.Content.Equals("InvokeExplorerStart"))
{
InvokeExplorer.Content = "InvokeExplorerStop";
StartInvokeExplorer();
//InvokeExplorer.Dispatcher.BeginInvoke(new InvokeExplorerDelegate(StartInvokeExplorer));
}
else
{
InvokeExplorer.Content = "InvokeExplorerStart";
StopInvokeExplorer();
}
}
public void StartInvokeExplorer()
{
if (XmlDataGrid.SelectedCells.Count > 0)
{
StartupCount = 1;
thread = new Thread(() =>
{
Starter(StartupCount);
});
thread.IsBackground = true;
thread.Start();
}
else
{
MessageBox.Show("Please select the recorded row to fetch the new data ");
InvokeExplorer.Content = "InvokeExplorerStart";
}
}
private void Starter(int cnt)
{
try
{
if (cnt > 0)
{
Hook.GlobalEvents().MouseClick += (sender, e) =>
{
if (e.Button == MouseButtons.Left)
{
Automate.Show(e);
}
};
Hook.GlobalEvents().MouseDoubleClick += (sender, e) =>
{
Automate.IsDoubleClick = true;
Automate.Show(e);
Automate.IsDoubleClick = false;
};
System.Windows.Forms.Application.Run(new ApplicationContext());
}
else
{
Hook.GlobalEvents().Dispose();
}
}
catch (Exception ex)
{
ErrorLog.Log(ex);
}
}
As from what I have understand, you want to stop the running thread.
This is how.
First, you need to create some stop logic. In your case, it would be some variable, like:
bool threadShouldRun;
and then inside your thread function, you should create a loop like:
void MyThreadFunc()
{
while(threadShouldRun)
{
threadWork();
Thread.Sleep(100);
}
}
When you want to stop the thread, just set your threadShouldRun variable to false.
Sleep is needed here. Without this, thread may use 100% of processor core.
You can use an AutoResetEvent in conjunction with a CancellationToken. Something along the line of (code not tested)
CancellationTokenSource cts;
AutoResetEvent autoResetEvent;
Thread thread;
public void ThreadStart()
{
cts = new CancellationTokenSource();
autoResetEvent = new AutoResetEvent();
thread = new Thread(new ParameterizedThreadStart(ThreadJob));
thread.Start(cts.Token);
}
public void ThreadStop()
{
cts?.Cancel();
thread?.Join();
cts?.Dispose();
autoResetEvent?.Dispose();
}
public static void ThreadJob(object obj)
{
var ct = (CancellationToken)obj;
while (!ct.IsCancellationRequested)
{
if(WaitHandle.WaitAny(new[] { tc.WaitHandle, autoResetEvent}) == 1)
{
// Do your stuff
}
}
}
public void PerformJobInThread()
{
autoResetEvent?.Set();
}
This way your thread will run until you call the ThreadStop method (actually, until you cancel your CancellationTokenSource) but you can still control when to "enable" it.
I want my program to wait after below line
frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
as above method is internally calling thread through StartProcessWithProgress() method . I want that thread to be completed before //code logic -2 line gets executed. At the same time, It should not stop UI update done by frmProgressBar.UpdateProgress(). How do I do this?
namespace NS1
{
public partial class frmMain : Form
{
private void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
//code logic - 2
MessageBox.Show("This is executing immediately.
I want to wait until above thread is complete");
}
}
public partial class frmProgressBar : Form
{
public void UpdateProgress(String strTextToDisplayOnProgress)
{
progressBar1.BeginInvoke(
new Action(() =>
{
progressBar1.Value++;
lblFileName.Text = strTextToDisplayOnProgress;
if (progressBar1.Value == progressBar1.Maximum)
{
this.Hide();
}
}));
}
public delegate void DelProgress();
public void StartProcessWithProgress(DelProgress delMethodCode, int maxCount)
{
InitializeProgress(maxCount);
Thread backgroundThread = new Thread(new ThreadStart(delMethodCode));
backgroundThread.Start();
}
}
public static class PullMSI
{
public static frmProgressBar ExtractByMSIName(String strProductFilePath, bool reNameMSI)
{
frmProgressBar frmProgressBar = new frmProgressBar();
frmProgressBar.StartProcessWithProgress(() =>
{
//StreamRader sr declaration and other code
while (!sr.EndOfStream)
{
//logic here
frmProgressBar.UpdateProgress("Copying sr.msiname");
}
}, 2);
return frmProgressBar;
}
}
}
I'm very surprised you haven't worked with any of these before but I would really recommend reading about threading in C# since it's fundamentally important to understand the intricacies and learning the language.
Below are three different ways you can achieve what you want:
1. Using reset events (further reading: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). If your C# version doesn't have the ManualResetEventSlim, replace it with ManualResetEvent and change Wait() with WaitOne()
class LockingWithResetEvents
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
public void Test()
{
MethodUsingResetEvents();
}
private void MethodUsingResetEvents()
{
ThreadPool.QueueUserWorkItem(_ => DoSomethingLong());
ThreadPool.QueueUserWorkItem(_ => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
_resetEvent.Set();
}
private void ShowMessageBox()
{
_resetEvent.WaitOne();
Console.WriteLine("Hello world.");
}
}
2) Using Task Parallel Library (TPL). Further reading: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
class LockingWithTPL
{
public void Test()
{
Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
3) Using Async/Await. Further reading: https://msdn.microsoft.com/en-us/library/hh191443.aspx
class LockingWithAwait
{
public void Test()
{
DoSomething();
}
private async void DoSomething()
{
await Task.Run(() => DoSomethingLong());
ShowMessageBox();
}
private async void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(10000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
Also good to know: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx), SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx), Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) and Interlocked (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).
If you're using .NET 4.0 (with VS2012) or above, you can do this quite easily with the Task Parallel Library and async-await:
private async void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = await Task.Run(() =>
PullMSI.ExtractByMSIName("products.txt", false));
MessageBox.Show(string.Format("Returned {0}", frmProgressBarObj.ToString());
}
For .NET 4, you'll need to add Microsoft.Bcl.Async.
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've got an app that has to do the following type of things, preferably on the GUI thread since that's where most of the action is taking place and there's no long-running ops:
Wait 1000
FuncA()
Wait 2000
FuncB()
Wait 1000
FuncC()
I realize I could use a timer with a state-machine style OnTick function, but that seems cumbersome:
int _state;
void OnTick(object sender, EventArgs e) {
switch (_state) {
case 0:
FuncA();
_timer.Interval = TimeSpan.FromSeconds(2);
_state = 1;
break;
case 1:
FuncB();
_timer.Interval = TimeSpan.FromSeconds(1);
_state = 2;
break;
case 2:
FuncC();
_timer.IsEnabled = false;
_state = 0;
}
}
Plus I'd like to be able to make it generic enough to do something like
RunSequenceOnGuiThread(new Sequence {
{1000, FuncA}
{2000, FuncB}
{1000, FuncC}};
Is there an idiomatic way to do this kind of thing? Given all the TPL stuff, or Rx, or even the computation expressions in F# I'd assume one exists, but I'm not finding it.
Observable.Concat(
Observer.Timer(1000).Select(_ => Func1()),
Observer.Timer(2000).Select(_ => Func2()),
Observer.Timer(1000).Select(_ => Func3()))
.Repeat()
.Subscribe();
The only thing you have to do to make this work, is make sure that your Func's return a value (even if that value is Unit.Default, i.e. nothing)
Edit: Here's how to make a generic version:
IObservable<Unit> CreateRepeatingTimerSequence(IEnumerable<Tuple<int, Func<Unit>>> actions)
{
return Observable.Concat(
actions.Select(x =>
Observable.Timer(x.Item1).Select(_ => x.Item2())))
.Repeat();
}
Here's a sketch of this in F#:
let f() = printfn "f"
let g() = printfn "g"
let h() = printfn "h"
let ops = [
1000, f
2000, g
1000, h
]
let runOps ops =
async {
for time, op in ops do
do! Async.Sleep(time)
op()
} |> Async.StartImmediate
runOps ops
System.Console.ReadKey() |> ignore
That's in a console app, but you can just call runOps on the GUI thread. See also this blog.
If you're using VS11/NetFx45/C#5, you can do a similar thing with C# async/await and a List of Tuple of Action delegates.
using the async CTP or .NET 4.5 (C# 5) it's REALLY easy using an async method and the await operator. This can be called directly on the UI thread and it will work as expected.
public async void ExecuteStuff()
{
await TaskEx.Delay(1000);
FuncA();
await TaskEx.Delay(2000);
FuncB();
await TaskEx.Delay(1000);
FuncC();
}
Here's a way to combine "yield return" and the reactive framework to give you a "poor man's async". Basically lets you "await" any IObservable. Here I just use it for timers since that's what you were interested in, but it you can have it "await" button clicks (using a Subject<Unit>) etc before moving on to the next thing as well.
public sealed partial class Form1 : Form {
readonly Executor _executor = new Executor();
public Form1() {
InitializeComponent();
_executor.Run(CreateAsyncHandler());
}
IEnumerable<IObservable<Unit>> CreateAsyncHandler() {
while (true) {
var i = 0;
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
}
}
IObservable<Unit> WaitTimer(double ms) {
return Observable.Timer(TimeSpan.FromMilliseconds(ms), new ControlScheduler(this)).Select(_ => Unit.Default);
}
}
public sealed class Executor {
IEnumerator<IObservable<Unit>> _observables;
IDisposable _subscription = new NullDisposable();
public void Run(IEnumerable<IObservable<Unit>> actions) {
_observables = (actions ?? new IObservable<Unit>[0]).Concat(new[] {Observable.Never<Unit>()}).GetEnumerator();
Continue();
}
void Continue() {
_subscription.Dispose();
_observables.MoveNext();
_subscription = _observables.Current.Subscribe(_ => Continue());
}
public void Stop() {
Run(null);
}
}
sealed class NullDisposable : IDisposable {
public void Dispose() {}
}
It's a slight modification of Daniel Earwicker's AsyncIOPipe idea: http://smellegantcode.wordpress.com/2008/12/05/asynchronous-sockets-with-yield-return-of-lambdas/
Interesting all the different responses. Here's a simple DIY option that doesn't depend on any other libraries, and doesn't hog thread resources unnecessarily.
Basically, for each action in your list, it creates an onTick function that executes that action, then recursively calls DoThings with the remaining actions and delays.
Here, ITimer is just a simple wrapper around DispatcherTimer (but it would work with a SWF Timer as well, or a mock timer for unit testing), and DelayedAction is just a Tuple with int Delay and Action action
public static class TimerEx {
public static void DoThings(this ITimer timer, IEnumerable<DelayedAction> actions) {
timer.DoThings(actions.GetEnumerator());
}
static void DoThings(this ITimer timer, IEnumerator<DelayedAction> actions) {
if (!actions.MoveNext())
return;
var first = actions.Current;
Action onTick = null;
onTick = () => {
timer.IsEnabled = false;
first.Action();
// ReSharper disable AccessToModifiedClosure
timer.Tick -= onTick;
// ReSharper restore AccessToModifiedClosure
onTick = null;
timer.DoThings(actions);
};
timer.Tick += onTick;
timer.Interval = first.Delay;
timer.IsEnabled = true;
}
}
If you don't want to delve into F# or reference Rx or use .Net 4.5 this is a simple viable solution.
Here's an example of how to test it:
[TestClass]
public sealed class TimerExTest {
[TestMethod]
public void Delayed_actions_should_be_scheduled_correctly() {
var timer = new MockTimer();
var i = 0;
var action = new DelayedAction(0, () => ++i);
timer.DoThings(new[] {action, action});
Assert.AreEqual(0, i);
timer.OnTick();
Assert.AreEqual(1, i);
timer.OnTick();
Assert.AreEqual(2, i);
timer.OnTick();
Assert.AreEqual(2, i);
}
}
And here's the other classes to make it compile:
public interface ITimer {
bool IsEnabled { set; }
double Interval { set; }
event Action Tick;
}
public sealed class Timer : ITimer {
readonly DispatcherTimer _timer;
public Timer() {
_timer = new DispatcherTimer();
_timer.Tick += (sender, e) => OnTick();
}
public double Interval {
set { _timer.Interval = TimeSpan.FromMilliseconds(value); }
}
public event Action Tick;
public bool IsEnabled {
set { _timer.IsEnabled = value; }
}
void OnTick() {
var handler = Tick;
if (handler != null) {
handler();
}
}
}
public sealed class MockTimer : ITimer {
public event Action Tick;
public bool IsEnabled { private get; set; }
public double Interval { set { } }
public void OnTick() {
if (IsEnabled) {
var handler = Tick;
if (handler != null) {
handler();
}
}
}
}
public sealed class DelayedAction {
readonly Action _action;
readonly int _delay;
public DelayedAction(int delay, Action action) {
_delay = delay;
_action = action;
}
public Action Action {
get { return _action; }
}
public int Delay {
get { return _delay; }
}
}
If you can use the C# 4.5 to do it, go with Firoso post: it's the best way accomplish that in C#, exactly what Async was built for.
However, if you can't, there might be some ways to do it. I'd do a "simple" manager to do it:
public partial class Form1 : Form
{
private TimedEventsManager _timedEventsManager;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_timedEventsManager
= new TimedEventsManager(this,
new TimedEvent(1000, () => textBox1.Text += "First\n"),
new TimedEvent(5000, () => textBox1.Text += "Second\n"),
new TimedEvent(2000, () => textBox1.Text += "Third\n")
);
}
private void button1_Click(object sender, EventArgs e)
{
_timedEventsManager.Start();
}
}
public class TimedEvent
{
public int Interval { get; set; }
public Action Action { get; set; }
public TimedEvent(int interval, Action func)
{
Interval = interval;
Action = func;
}
}
public class TimedEventsManager
{
private readonly Control _control;
private readonly Action _chain;
public TimedEventsManager(Control control, params TimedEvent[] timedEvents)
{
_control = control;
Action current = null;
// Create a method chain, beginning by the last and attaching it
// the previous.
for (var i = timedEvents.Length - 1; i >= 0; i--)
{
var i1 = i;
var next = current;
current = () =>
{
Thread.Sleep(timedEvents[i1].Interval);
// MUST run it on the UI thread!
_control.Invoke(new Action(() => timedEvents[i1].Action()));
if (next != null) next();
};
}
_chain = current;
}
public void Start()
{
new Thread(new ThreadStart(_chain)).Start();
}
}
Beware that this example is Winforms specific (uses Control.Invoke()). You will need a slightly different version for WPF, which uses the thread dispatcher to achieve the same thing. (if my memory doesn't fail me, you also can use Control.Dispatcher.Invoke(), but keep in mind that it is a different control)