Last active
August 29, 2015 14:00
-
-
Save tayl0r/11139132 to your computer and use it in GitHub Desktop.
c# command line app - threading & async
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static void Main(string[] args) { | |
// this news a new instance of TournamentService/BaseService, | |
// which runs StartTickLoop, which creates the new threads | |
BaseService.OnStart(true); | |
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { | |
Console.WriteLine("captured ctrl-c, setting close flag"); | |
e.Cancel = true; | |
CommandLineApp.close = true; | |
}; | |
// main thread run loop | |
while (BaseService.IsRunning()) { | |
//Console.WriteLine("tick 2"); | |
System.Threading.Thread.Sleep((int)BaseService.TICK_TIME); | |
if (CommandLineApp.close) { | |
Console.WriteLine("close flag set, ending gracefully"); | |
BaseService.OnStop(); | |
} | |
} | |
Console.WriteLine("Press any key to close..."); | |
Console.ReadLine(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BaseService { | |
ManualResetEvent _shutdownEvent = new ManualResetEvent(false); | |
CancellationTokenSource _cancellationTokenSouce = new CancellationTokenSource(); | |
static List<Thread> _threads = new List<Thread>(); | |
public string _name; | |
#if TOURNAMENT_PROCESSOR | |
public const long TICK_TIME = 5000; | |
#endif | |
#region static to handle the creation and start of this service | |
static BaseService _proc; | |
public static void OnStart(bool writeToConsole) { | |
// this is how i do different types of commandline/services | |
// they share the same base code & same VS project (different build configuration) | |
#if TOURNAMENT_PROCESSOR | |
_proc = new TournamentService(writeToConsole); | |
#endif | |
_proc.StartTickLoop(); | |
} | |
public static bool IsRunning() { | |
foreach (var thread in _threads) { | |
if (thread.IsAlive == true) { | |
return true; | |
} | |
if (thread.ThreadState != System.Threading.ThreadState.Stopped) { | |
return true; | |
} | |
} | |
return false; | |
} | |
public void StartTickLoop() { | |
#if TOURNAMENT_PROCESSOR | |
// multiple threads | |
foreach (var kvp in LeaderboardManager.TOURNAMENT_CONFIG_DATA) { | |
var theme = kvp.Key; | |
var config = kvp.Value; | |
var thread = new Thread(ThreadStart); | |
thread.Name = string.Format("Thread_{0}", theme.ToString()); | |
thread.IsBackground = true; | |
thread.Start(theme); | |
_threads.Add(thread); | |
} | |
#else | |
// single thread | |
var thread = new Thread(ThreadStart); | |
thread.Name = "Thread"; | |
thread.IsBackground = true; | |
thread.Start(); | |
_threads.Add(thread); | |
#endif | |
} | |
public void StopTickLoop() { | |
Logger.L("stop tick loop"); | |
_shutdownEvent.Set(); | |
_cancellationTokenSouce.Cancel(); | |
// give the thread some time to stop (900,000 ms = 15 minutes | |
foreach (var thread in _threads) { | |
if (!thread.Join(900000)) { | |
thread.Abort(); | |
} | |
} | |
} | |
void ThreadStart(object data) { | |
var stopwatch = new Stopwatch(); | |
#if TOURNAMENT_PROCESSOR | |
var theme = (SlotMachineThemeType)data; | |
var name = theme.ToString(); | |
var child = new TournamentChildService(theme); | |
#endif | |
// per thread run loop | |
while (!_shutdownEvent.WaitOne(0)) { | |
stopwatch.Restart(); | |
//await Logger.LA("Tick"); | |
//Console.WriteLine("tick 1"); | |
#if TOURNAMENT_PROCESSOR | |
child.Tick(_cancellationTokenSouce.Token); | |
#else | |
Tick(); | |
#endif | |
if (stopwatch.ElapsedMilliseconds < TICK_TIME) { | |
var waitTime = TICK_TIME - stopwatch.ElapsedMilliseconds; | |
Logger.L(string.Format("########## {1}: Waiting {0:N2} seconds for next tick.", waitTime/1000, name)); | |
Thread.Sleep((int)(waitTime)); | |
} else { | |
Logger.L(string.Format("########## {1}: tick time: {0:N2}s", stopwatch.Elapsed.TotalSeconds, name)); | |
} | |
} | |
Logger.L("Stopped: " + name + ", " + Thread.CurrentThread.Name + ", " + Thread.CurrentThread.ManagedThreadId); | |
} | |
protected virtual void Tick() { | |
// single thread service uses this Tick | |
// multiple thread service (like our TournamentProcessor uses the child service Tick) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class TournamentService : BaseService { | |
public TournamentService(bool writeToConsole) | |
#if RELEASE | |
: base("TournamentProcessor", writeToConsole) { | |
#else | |
: base("TournamentProcessor-Dev", writeToConsole) { | |
#endif | |
} | |
} | |
class TournamentChildService { | |
TournamentProcessor _t; | |
private SlotMachineThemeType _theme; | |
public TournamentChildService(SlotMachineThemeType theme) { | |
_theme = theme; | |
LeaderboardManager.CreateTournamentsForTheme(_theme).Wait(); | |
// wait 5 seconds so dynamo db indexes have a chance to catch up (since reads from a global secondary index are forced to be eventually consistant) | |
Thread.Sleep(5000); | |
_t = new TournamentProcessor(_theme); | |
} | |
public void Tick(CancellationToken cancellationToken) { | |
try { | |
// this is how i'm going from non-async to async mode | |
// pass in the cancellation token so in our 5+ minute sleep we can break out via ctrl-c | |
// in TickAsync, everything stays with async methods and i handle all exceptions inside | |
_t.TickAsync(cancellationToken).Wait(); | |
} catch (Exception) { | |
if (cancellationToken.IsCancellationRequested == false) { | |
throw; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment