I've looked around and apparently I've got the choice between these libraries/ solutions:
One:
public void Awake() {
Invoke("InvokeAndPrint", 2);
}
void InvokeAndPrint() {
print("InvokeAndPrint 2");
}
Two:
void Start() {
StartCoroutine(WaitAndPrint(2.0F));
}
IEnumerator WaitAndPrint(float waitTime) {
yield return new WaitForSeconds(waitTime);
print("WaitAndPrint " + Time.time);
}
I want to know if there is any other better way?
This is a summary of my comments above
The only other method I can think of is old-school take note of the time at start; then in your Update() method check for elapsed time. You essentially do everything yourself. Though way more verbose than the above examples, it's type-safe and does not require any extra threads or thread job objects.
Simplest
First we need some fields defined:
private DateTime _start;
private bool _done;
In your start take note of the time:
void Start()
{
_start = DateTime.Now;
}
...then in your update check to see how much time has elapsed. If its greater than your timeout of say 2 seconds, fire off what-ever you wish to do - in this case print():
void Update()
{
if (! _done && (DateTime.Now - _start).TotalSeconds >= 2)
{
print("hello world");
_done = true;
}
}
That's it.
Re-usable Code
You'll probably find that there are many places where there is a need for this so wouldn't it be groovy if there was a way to cut down on repeated code. Perhaps a class to wrap it up in?
class DelayedJob
{
private readonly TimeSpan _delay;
private readonly Action _action;
private readonly DateTime _start;
public DelayedJob(TimeSpan delay, Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
_delay = delay;
_action = action;
_start = DateTime.Now;
}
/// <summary>
/// Updates this instance.
/// </summary>
/// <returns>true if there is more work to do, false otherwise</returns>
public bool Update()
{
if (DateTime.Now - _start >= _delay)
{
_action();
return false;
}
return true;
}
}
Then you could do something like this:
void Start()
{
_job = new DelayedJob(TimeSpan.FromSeconds(2), ()=> print("hello"));
}
...after updating your Update() accordingly:
void Update()
{
if (_job != null && !_job.Update())
{
_job = null;
}
}
Multiple Jobs
It's just a matter of placing them in a collection and processing it at runtime.
private List<DelayedJob> _jobs;
void Start()
{
_jobs = new List<DelayedJob>
{
new DelayedJob(TimeSpan.FromSeconds(2), () => print("star wars")),
new DelayedJob(TimeSpan.FromSeconds(3f), () => print("is coming!"))
};
}
...a few alterations to Update():
void Update()
{
bool again;
do
{
again = false;
// you probably want to optimise this so that we don't check the same items
// at the start again after removing an item
foreach (var delayedJob in _jobs)
{
if (!delayedJob.Update())
{
_jobs.Remove(delayedJob);
again = true; // start over
break;
}
}
}
while (again);
}
Related
Is there a way to write a safe one-second off-delay timer class in C# using exactly one System.Threading.Timer object?
Alternatively, what would be the simplest solution in general, assuming the input can get turned on and off a lot faster than once per second?
An off-delay timer could be described by this interface:
public interface IOffDelay
{
/// <summary>
/// May be set to any value from any thread.
/// </summary>
bool Input { get; set; }
/// <summary>
/// Whenever Input is true, Output is also true.
/// The Output is only false at startup
/// or after the Input has been continuously off for at least 1 second.
/// </summary>
bool Output { get; }
}
This is my first attempt:
public sealed class OffDelay : IOffDelay, IDisposable
{
public OffDelay()
{
timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public bool Input
{
get
{
lock (locker)
return _input;
}
set
{
lock (locker)
{
if (value == _input)
return;
_input = value;
if (_input == false)
timer.Change(1000, Timeout.Infinite);
else
{
_output = true;
timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
}
}
private bool _input;
public bool Output
{
get
{
lock (locker)
return _output;
}
}
private bool _output;
private readonly Timer timer;
private readonly object locker = new object();
private void TimerCallback(object state)
{
lock (locker)
_output = false;
}
}
I can see that there is a race condition in this solution:
At the end of a one-second off period, the timer schedules the callback to run.
Someone quickly sets and resets the input, restarting the timer.
The callback now finally runs, checks the input and sets the output to false even though it should be true for another second.
Edit
Peter Duniho provided the correct answer, but it turns out I'm terrible at asking the right question. The OffDelay class should also do some operation when the output changes to false. Here is the modified code adapting Peter's basic principle:
public sealed class OffDelay : IOffDelay, IDisposable
{
public OffDelay()
{
timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public bool Input
{
get
{
lock (locker)
return _input;
}
set
{
lock (locker)
{
if (value == _input)
return;
_input = value;
if (_input == true)
_output = true;
else
{
stopwatch.Restart();
if (!timerRunning)
timer.Change(1000, Timeout.Infinite);
}
}
}
}
private bool _input;
public bool Output
{
get
{
lock (locker)
return _output;
}
}
private bool _output;
private readonly object locker = new object();
private readonly Timer timer;
private readonly Stopwatch stopwatch = new Stopwatch();
private bool timerRunning;
private void TimerCallback(object state)
{
lock (locker)
{
if (_input == true)
timerRunning = false;
else
{
var remainingTimeMs = 1000 - stopwatch.ElapsedMilliseconds;
if (remainingTimeMs > 0)
timer.Change(remainingTimeMs, Timeout.Infinite);
else
{
_output = false;
timerRunning = false;
DoSomething();
}
}
}
}
private void DoSomething()
{
// ...
}
}
Without a good Minimal, Complete, and Verifiable code example that clearly illustrates the question, including showing exactly what the context is and what constraints might exist, it's impossible to know for sure what answer would work for you.
But based on what you've included in your question, I would change your implementation so it doesn't rely on the timer:
public sealed class OffDelay : IOffDelay
{
public bool Input
{
get { lock (locker) return _input; }
set
{
lock (locker)
{
if (value == _input)
return;
_input = value;
_lastInput = DateTime.UtcNow;
}
}
}
private bool _input;
public bool Output
{
get { lock (locker) return (DateTime.UtcNOw - _lastInput).TotalSeconds < 1; }
}
private DateTime _lastInput;
}
Note that the above is susceptible to clock changes on the computer. If you have a need to work independently of the clock, you can replace DateTime.UtcNow with a Stopwatch instance, call Reset() on each change to the Input property, and use the Elapsed property of the Stopwatch to determine the length of time since last input.
This question already has answers here:
Use Unity API from another Thread or call a function in the main Thread
(5 answers)
Closed 6 years ago.
I'm having trouble with a UnityEngine version. (Can't upgrade, game is not mine)
The server RANDOMLY crashes when a specific UnityEngine method is used in a timer/thread (It was fixed in a version, I read It)
It happens totally random, I get a crash log, that starts from the timer/thread and ends at a UnityEngine method. (This never happens when I use It in the main thread)
My question is that Is It possible somehow to call the method from the main thread if the current thread != with the main thread?
Any help is appreciated
This Loom class is able to call the specific method from the Main thread, this is how you do It:
public class Loom : MonoBehaviour
{
public static int maxThreads = 10;
static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
public void Awake()
{
_current = this;
initialized = true;
}
static bool initialized;
static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
}
}
private List<Action> _actions = new List<Action>();
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action)
{
QueueOnMainThread(action, 0f);
}
public static void QueueOnMainThread(Action action, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
}
public static Thread RunAsync(Action a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch
{
}
finally
{
Interlocked.Decrement(ref numThreads);
}
}
public void OnDisable()
{
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
public void Start()
{
}
List<Action> _currentActions = new List<Action>();
// Update is called once per frame
public void Update()
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
foreach (var a in _currentActions)
{
a();
}
lock (_delayed)
{
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
foreach (var item in _currentDelayed)
_delayed.Remove(item);
}
foreach (var delayed in _currentDelayed)
{
delayed.action();
}
}
}
//Usage
public void Call()
{
if (Thread.CurrentThread.ManagedThreadId != TestClass.MainThread.ManagedThreadId)
{
Loom.QueueOnMainThread(() => {
Call();
});
return;
}
Console.WriteLine("Hello");
}
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)
I have a WPF user control called TimerUserControl where contains a timer. And I have another user control where show questions, this one has a NextQuestion function.
The timer has 2 minutes like an interval, and I'd like to invoke the NextQuestion function when it has done. I think I have to use delegates, but I'm not sure.
UPDATE 1:
public partial class TimeUserControl : UserControl
{
public int _totalSeconds;
public int _secondsRemaining;
public DispatcherTimer timerSecondsLeft;
public TimeUserControl()
{
InitializeComponent();
timerSecondsLeft = new DispatcherTimer();
timerSecondsLeft.Tick += new EventHandler(timerSecondsLeft_Tick);
timerSecondsLeft.Interval = new TimeSpan(0, 0, 1);
}
public bool TimesUp
{
get;
set;
}
public void SetSeconds(int seconds)
{
timerSecondsLeft.Stop();
if (seconds == 0)
{
TimeTextBlock.Text = "There's no time! Hurray";
}
else
{
_totalSeconds = seconds;
_secondsRemaining = seconds;
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _totalSeconds);
timerSecondsLeft.Start();
}
}
public void timerSecondsLeft_Tick(object sender, EventArgs e)
{
_secondsRemaining--;
if (_secondsRemaining <= 0)
{
timerSecondsLeft.Stop();
TimesUp = true;
TimeTextBlock.Text = "Time's up. Press Enter to next problem.";
// HERE WILL INVOKE NEXTQUESTION FUNCTION
}
else
{
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
}
}
}
Look in the code, the comment is this possible ussing delegates?
So you need to do a few things. You have to add some code to you're user control.
// Declare this outside your usercontrol class
public delegate void TimerExpiredEventHandler(object sender, EventArgs e);
This is what needs to be added to your code for the user control.
public partial class TimerUserControl : UserControl
{
public event TimerExpiredEventHandler Expired;
public void OnExpired(EventArgs e)
{
if (Expired != null)
Expired(this, e);
}
public void timerSecondsLeft_Tick(object sender, EventArgs e)
{
_secondsRemaining--;
if (_secondsRemaining <= 0)
{
timerSecondsLeft.Stop();
TimesUp = true;
TimeTextBlock.Text = "Time's up. Press Enter to next problem.";
// Fire the event here.
OnExpired(EventArgs.Empty);
}
else
{
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
}
}
}
Now you need to subscribe to this event inside whatever is calling this usercontrol in the first place.
public partial class ParentForm : Form
{
private void ParentForm_Load(object sender, EventArgs e)
{
var timer = new TimerUserControl();
//Subscribe to the expired event that we defined above.
timer.Expired += new EventArgs(Timer_Expired);
}
public void Timer_Expired(object sender, EventArgs e)
{
//Handle the timer expiring here. Sounds like you are calling another function, so do that here.
}
}
Use the TreeHelper to hunt up the tree for a shared Parent and then down the tree for the User Control you want. Something like this pseudo code:
this.Timer = new System.Windows.Threading.DispatcherTimer
{
Interval = new TimeSpan(0, 0, 1)
};
this.Timer.Tick += (s, e) =>
{
var _Control = s as MyFirstControl;
var _Other = LogicalTreeHelper.GetChildren(_Control.Parent)
.Cast<FrameworkElement>().Where(x => x.Name == "FindIt")
.First<MySecondControl>();
_Other.DoMethod();
};
Best of luck!
i would probably break out the functionality of the timer control here; something like this (note: i am writing this on-the-fly so let me know if it doesn't work as-is, and i will help correct any issues):
// a simple delegate to report the amount of time remaining
// prior to the expiration of the major tick interval; zero
// indicates that this major tick has elapsed.
public delegate void delegateMajorMinorTimerTick
(
int TimeRemaining_sec, ref bool CancelTimer
);
// you could use milliseconds for the interval settings to get
// better granularity, or you could switch to setting the major
// interval instead, however that approach would require a bit
// more checking to make sure the control has sane settings.
public class MajorMinorTimer
{
// this sets the interval in seconds between the
// "minor" ticks used for intermediate processing
// these are the "inner" intervals of the timer
private int myMinorTickInterval_sec;
public int MinorTickInterval_sec
{
get { return myMinorTickInterval_sec; }
}
// this sets the number of minor ticks between the
// expiration of the major interval of the timer.
// the "outer" interval of the timer
private int myMinorTicksPerMajorTick;
public int MinorTicksPerMajorTick
{
get { return myMinorTicksPerMajorTick; }
}
public MajorMinorTimer
(
int parMinorTickInterval_sec,
int parMinorTicksPerMajorTick
)
{
MinorTickInterval_sec = parMinorTickInterval_sec;
MinorTicksPerMajorTick = parMinorTicksPerMajorTick;
}
private DispatcherTimer myBackingTimer;
private int myMinorTickCount;
public void Start()
{
// reset the minor tick count and start the dispatcher
// timer with some reasonable defaults.
myMinorTickCount = 0;
myBackingTimer =
new DispatcherTimer
(
TimeSpan.FromSeconds(MinorTickInterval_sec),
DispatcherPriority.Normal,
new EventHandler(myBackingTimer_Tick),
Dispatcher.CurrentDispatcher
);
myBackingTimer.Start();
}
public event delegateMajorMinorTimerTick onTick;
private bool FireMajorMinorTimerTick(int TimeRemaining_sec)
{
// allows the timer sink to cancel the timer after this
// call; just as an idea, also could be handled with a
// call to Stop() during the event, but this
// simplifies handling a bit (at least to my tastes)
bool CancelTimer = false;
if (onTick != null)
onTick(TimeRemaining_sec, ref CancelTimer);
return CancelTimer;
}
private void myBackingTimer_Tick(object Sender, EventArgs e)
{
// since we are using a DispatchTimer with settings that should
// do not suggest the possibility of synchronization issues,
// we do not provide further thread safety. this could be
// accomplished in the future if necessary with a lock() call or
// Mutex, among other methods.
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
bool Cancel =
FireMajorMinorTimerTick(TicksRemaining * MinorTickInterval_sec);
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
public void Stop()
{
myBackingTimer.Stop();
}
}
then, assuming, say, a Quiz control, the timer is used like so:
public void QuestionTimerSetup()
{
// sets up a timer to fire a minor tick every second
// with a major interval of 5 seconds
MajorMinorTimer timerQuestion = new MajorMinorTimer(1, 5);
timerQuestion.onTick +=
new delegateMajorMinorTimerTick(QuestionControl_QuestionTimerTick);
}
// ...
public void QuestionControl_OnTick(int TimeRemaining_sec, ref bool CancelTimer)
{
if (TimeRemaining_sec > 0)
{
tblockQuizStatus.Text =
string.Format("There are {0} seconds remaining.", TimeRemaining_sec);
}
else
{
// just for an example
if (NoMoreQuestions)
{
CancelTimer = true;
HandleEndOfQuiz();
tblockQuizStatus.Text =
"Time's up! The quiz is complete!";
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to the next problem.";
}
}
}
another option (rather than, or in addition to, events) in implementing this might be to add an Action taking the time remaining in the major interval for the minor interval action, an Action for the major interval action, and a Func<bool> that checks the stop condition, allowing the user to perform the desired actions in that way. like this:
public class MajorMinorTimer
{
public MajorMinorTimer
(
int parMinorTimerInterval_sec,
int parMinorTicksPerMajorTick,
Action<int> parMinorTickAction,
Action parMajorTickAction,
Func<bool> parShouldStopFunc
)
{
myMinorTimerInterval_sec = parMinorTimerInterval_sec;
myMinorTicksPerMajorTick = parMinorTicksPerMajorTick;
myMinorTickAction = parMinorTickAction;
myMajorTickAction = parMajorTickAction;
myShouldStopFunc = parShouldStopFunc;
}
private Action<int> myMinorTickAction;
private Action myMajorTickAction;
private Func<bool> myShouldStopFunc;
private void myBackingTimer_OnTick()
{
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
if (TicksRemaining == 0)
myMajorTickAction();
else
myMinorTickAction(TicksRemaining * MinorTickInterval_sec);
bool Cancel = myShouldStopFunc();
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
}
and then in the quiz code instead of hooking up the event do something like:
public void QuestionTimerSetup()
{
MajorMinorTimer timerQuestion =
new MajorMinorTimer
(
1,
5,
// major interval action
(SecsRemaining) =>
{
tblockQuizStatus.Text =
string.Format
(
"There are {0} seconds remaining.", SecsRemaining
);
},
// minor interval action
() =>
{
if (NoMoreQuestions)
{
tblockQuizStatus.Text =
"Time's up! This completes the quiz!";
HandleEndOfQuiz();
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to next question.";
}
},
// timer cancel check function
() =>
IsEndOfQuizHandled()
);
}
I would like to run code alternatively, so I could stop execution at any moment. Is this code safe?
static class Program
{
static void Main()
{
var foo = new Foo();
//wait for interaction (this will be GUI app, so eg. btnNext_click)
foo.Continue();
//wait again etc.
foo.Continue();
foo.Continue();
foo.Continue();
foo.Continue();
foo.Continue();
}
}
class Foo
{
public Foo()
{
new Thread(Run).Start();
}
private void Run()
{
Break();
OnRun();
}
protected virtual void OnRun()
{
for (var i = 0; i < 5; i++)
{
Console.WriteLine(i);
Break();
}
//do something else and break;
}
private void Break()
{
lock (this)
{
Monitor.Pulse(this);
Monitor.Wait(this);
}
}
public void Continue()
{
lock (this)
{
Monitor.Pulse(this);
Monitor.Wait(this);
}
}
}
Of course I know, that now the application will never ends, but that's not the point.
I need this, because I would like to present steps in some kind of an algorithm and describe what is going on in particular moment, and making everything in one thread would lead to many complications even when using small amount of loops in the code. For example those lines:
for (var i = 0; i < 5; i++)
{
Console.WriteLine(i);
Break();
}
should be then replaced with:
if (this.i < 5)
{
Console.WriteLine(i++);
}
And that is just a small example of what I want to present. The code will be more complicated than a dummy for loop.
I recommend you check out this blog post about implementing fibers.
Code (In case the site goes down.)
public class Fiber
{
private readonly Stack<IEnumerator> stackFrame = new Stack<IEnumerator>();
private IEnumerator currentRoutine;
public Fiber(IEnumerator entryPoint)
{
this.currentRoutine = entryPoint;
}
public bool Step()
{
if (currentRoutine.MoveNext())
{
var subRoutine = currentRoutine.Current
as IEnumerator;
if (subRoutine != null)
{
stackFrame.Push(currentRoutine);
currentRoutine = subRoutine;
}
}
else if (stackFrame.Count > 0)
{
currentRoutine = stackFrame.Pop();
}
else
{
OnFiberTerminated(
new FiberTerminatedEventArgs(
currentRoutine.Current
)
);
return false;
}
return true;
}
public event EventHandler<FiberTerminatedEventArgs> FiberTerminated;
private void OnFiberTerminated(FiberTerminatedEventArgs e)
{
var handler = FiberTerminated;
if (handler != null)
{
handler(this, e);
}
}
}
public class FiberTerminatedEventArgs : EventArgs
{
private readonly object result;
public FiberTerminatedEventArgs(object result)
{
this.result = result;
}
public object Result
{
get { return this.result; }
}
}
class FiberTest
{
private static IEnumerator Recurse(int n)
{
Console.WriteLine(n);
yield return n;
if (n > 0)
{
yield return Recurse(n - 1);
}
}
static void Main(string[] args)
{
var fiber = new Fiber(Recurse(5));
while (fiber.Step()) ;
}
}
"...this will be GUI app..."
Then you probably do not want and will not have sequential code like above in Main().
I.e. the main GUI thread will not execute a serial code like above, but generally be idle, repainting, etc. or handling the Continue button click.
In that event handler you may better use an Auto|ManualResetEvent to signal the worker to proceed.
In the worker, just wait for the event.
I would suggest that any time one considers using Monitor.Wait(), one should write code so that it would work correctly if the Wait sometimes spontaneously acted as though it received a pulse. Typically, this means one should use the pattern:
lock(monitorObj)
{
while(notYetReady)
Monitor.Wait(monitorObj);
}
For your scenario, I'd suggest doing something like:
lock(monitorObj)
{
turn = [[identifier for this "thread"]];
Monitor.PulseAll(monitorObj);
while(turn != [[identifier for this "thread"]])
Monitor.Wait(monitorObj);
}
It is not possible for turn to change between its being checked whether it's the current thread's turn to proceed and the Monitor.Wait. Thus, if the Wait isn't skipped, the PulseAll is guaranteed to awaken it. Note that the code would work just fine if Wait spontaneously acted as though it received a pulse--it would simply spin around, observe turn wasn't set for the current thread, and go back to waiting.