I am coding a Forex Trading robot, and I am running an outOfMemory exception after some time (around 2 hours) using BlockingCollection.
I basically have 1 queue pair Trade chart, that are added into a dict:
private Dictionary<string, BlockingCollection<tick>> tickQueues = new Dictionary<string, BlockingCollection<tick>>();
I check the memory dump after one hour, and I can see the following items are piling up:
Count Size(bytes) Inclusive Size
ThreadPoolWorkQueue+QueueSegment 22,951 24,236,256 40,316,868
QueueUserWorkItemCallback 689,838 13,796,760 16,081,272
TimerQueueTimer 11,160 713,772 2,355,736
I have a timer that is responsible to add data to the Queue:
private void TickTimer_tick(object source, ElapsedEventArgs e) {
if (Monitor.TryEnter(LockTimerTick, GlobalSettings.APISleepDelayMSTick)) {
updateLockFailCount = 0;
try {
tick t = new tick(DateTime.Now, d.bid, d.ask);
lastBid = d.bid;
lastAsk = d.ask;
t.pair = Inst.pair;
//myTickQueue.TryAdd(t);
if (!myTickQueue.TryAdd(t)) {
functions.Logger.log("Error when adding Tick on Queue for " + Inst.pair+ " Maybe Queue is full", "SHMAPI", LOGLEVEL.WARN);
}
} catch (Exception E) {
functions.Logger.log("Error happened when refreshing tick data: " + E.Message, "SHMAPI", LOGLEVEL.ERROR);
} finally {
Monitor.Exit(LockTimerTick);
}
} else {
updateLockFailCount++;
int sev = LOGLEVEL.TRACE;
if (updateLockFailCount == 10) { sev = LOGLEVEL.DEBUG; }
if (updateLockFailCount==50) { sev = LOGLEVEL.WARN; }
if (updateLockFailCount % 100 == 0 && updateLockFailCount>=100) { sev = LOGLEVEL.ERROR; }
functions.Logger.log("Could not get lock to refresh tick data for symbol "+Symbol, "SHMAPI", sev);
}
}
And finally, my task that checks the Q:
public void startQueueTask(string Pair) {
if (!tickQueues.ContainsKey(Pair.ToUpper())) {
tickQueues.Add(Pair.ToUpper(), new BlockingCollection<tick>(GlobalSettings.tickQueueSize));
if (!MTAPIs.ContainsKey(Pair.ToUpper())) {
throw new Exception("API for pair " + Pair + " Should be initialized !!");
}
MTAPIs[Pair.ToUpper()].setTickQueue(tickQueues[Pair.ToUpper()]);
functions.Logger.log("Starting " + Pair + " Queue Task", "TICKPROCESSING", LOGLEVEL.DEBUG);
Task.Run(() => {
foreach (tick tick in tickQueues[Pair.ToUpper()].GetConsumingEnumerable()) {
try {
onTick(tick);
} catch (Exception E) {
functions.Logger.log("Error processing tick for symbol " + tick.pair + " " + E.Message, "TICKPROCESSING", LOGLEVEL.ERROR);
functions.printException(E);
}
}
functions.Logger.log("Exiting Queue Task", "TICKPROCESSING", LOGLEVEL.ERROR);
});
} else {
functions.Logger.log("Skipping " + Pair + " Queue Task because already exists", "TICKPROCESSING", LOGLEVEL.DEBUG);
}
}
I am not really sure why I am getting OOM, but it looks similar to:
http://blogs.microsoft.co.il/bnaya/2012/02/26/real-life-story-blocking-collection/
But I am not using parallel here... My Queues are empty though since week end market is closed.
Would using another timer with TryDequeue a better approach ?
Any advice would be welcome !
I switched timer to manual like this:
private void TickTimer_tick(object source, ElapsedEventArgs e) {
try {
//...
} finally {
TickTimer.Start();
}
}
And it seemed to have resolved my issue.
I am also making sure to send ticks in the Q, and they are filtered by duplicates by the Receiver, just so the Queing thread is never pending for too long as well.
Thanks for the pointer !
Related
My problem is that I have multiple Forms Controls that I need to make visible/invisible/change text, based on the output of my code.
Obviously this is very easy to achieve but the code is ridiculously long due to how I've set it up.
I have 3 image boxes containing a red, green and orange 'light'.
When the ping action is started (button click or a timer) all the controls need to be set like so:
// ping 1
redLight1.Visible = true;
greenLight1.Visible = false;
orangeLight1.Visible = false;
status_Lbl1.Text = "Initiated...";
I need to do this 9 times, and the code looks a bit meh to me having this repeated this many times.
I have a ping object that sends a ping every second for x amount of time. If all of the pings are sent and received successfully then an imagebox containing a green circle becomes visible greenLight1.Visible = true, while all others are set redLight1.Visible = false, orangeLight1.Visible = false, etc.
I have 9 of these sets of 'traffic lights', with a different IP being pinged and a different outcome for each.
I feel there must be a way to iteratively change the values of each of these boxes using the fact they all follow the same naming convention with just a different number on the end corresponding to the ping object they represent.
Here's a more visual idea of what I want to achieve.
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// int counter = 0
if (e.Cancelled == true)
{
status_Lbl1.Text = "Cancelled";
}
else if (e.Error != null)
{
status_Lbl1.Text = "Error: " + e.Error.Message;
}
else
{
foreach (Ping pingObj in pings)
{
if (pingObj.SuccessfulPings == 0)
{
Debug.WriteLine("Ping Object: " + pingObj.Fqdn + " failed to successfully ping");
// greenLight[i].Visible = false;
// orangeLight[i].Visible = false;
// redLight[i].Visible = true;
// counter++
}
else if (pingObj.FailedPings != 0)
{
Debug.WriteLine("Ping Object: " + pingObj.Fqdn + " failed to successfully ping: " + pingObj.FailedPings + " times.");
// greenLight[i].Visible = false;
// orangeLight[i].Visible = true;
// redLight[i].Visible = false;
// counter++
}
else
{
Debug.WriteLine("Ping Object: " + pingObj.Fqdn + " succesfully pinged: " + pingObj.SuccessfulPings + " times.");
// greenLight[i].Visible = false;
// orangeLight[i].Visible = false;
// redLight[i].Visible = false;
// counter++
}
}
}
}
Here's the method that creates/uses the ping objects just in case that is necessary
private async void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
// Create background worker
BackgroundWorker worker = (BackgroundWorker)sender;
// Write to log file
await file.WriteAsync("Starting job...\n");
await file.WriteAsync("Requested amount of pings: " + count + "\n");
// Create date object for logs
DateTime localDate = DateTime.Now;
for (int i = 0; i < count; i++)
{
// Create ping objects
System.Net.NetworkInformation.Ping pinger = new System.Net.NetworkInformation.Ping();
PingReply pingReply;
try
{
foreach (Ping pingObj in pings)
{
try
{
pingReply = pinger.Send(pingObj.IpAddress);
// Write log file
await file.WriteLineAsync(localDate.TimeOfDay + " | Friendly Name " + pingObj.FriendlyName + " | Ping: " + pingReply.Address + " | Status " + pingReply.Status + " | Time: " + pingReply.RoundtripTime);
if (pingReply.Status.ToString().Contains("Success"))
{
pingObj.SuccessfulPings += 1;
}
else // Unsuccessful ping has been sent
{
pingObj.FailedPings += 1;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
wait(1000);
}
catch (Exception b)
{
Debug.WriteLine(b.ToString());
}
}
}
catch (Exception a)
{
Debug.WriteLine(a.ToString());
}
}
You can use Controls.Find() and "search by name" with the recurse option:
Control ctl = this.Controls.Find("greenLight" + counter, true).FirstOrDefault() as Control;
if (ctl != null) {
ctl.Visible = false;
}
I have a task that is saving my DataTable in to a file every 10 seconds:
void WriteTask(DataTable dt, bool final)
{
if (final)
{
exiting = false;
}
while (!exiting)
{
lock (users)
{
try
{
dt.WriteXml(filePath+ "users.xml_be");
File.Copy(#$"{filePath}users.xml_be", #$"{filePath}users.xml_betemp");
File.Replace(#$"{filePath}users.xml_betemp", #$"{filePath}users.xml", #$"{filePath}users.xml_be2");
if (final)
exiting = true;
}
catch (Exception ex)
{
try
{
File.AppendAllText($"{filePath}log.txt", $"{DateTime.Now.ToString()} {ex.Message}" + Environment.NewLine);
File.AppendAllText($"{filePath}log.txt", $"{ex.StackTrace}" + Environment.NewLine);
File.AppendAllText($"{filePath}log.txt", "////////////////////////////////////////////////////////////////" + Environment.NewLine);
}
catch (Exception)
{
client.SendMessage(tChannel, ErrorMes1);
client.SendMessage(tChannel, $"{ex.Message}|{ex.StackTrace}");
}
}
}
Thread.Sleep(10000);
}
}
And recently I noticed that file are not getting changed. Weirdiest think is that when i open file in notepad++, its saying that the file has been chaged and it wants to reload it, but afte I reload the fle there is not chnages inside. Could it be because file is getting pretty big? It have around 1mil of lines.
Update:
So, I created async tasks for all things that have to do with datatables:
Task that is in a unending loop, saving every 30 seconds
async void WriteTask(DataTable dt, bool final)
{
if (final)
{
exiting = false;
}
while (!exiting)
{
Task UvalClockTaksTask = new Task(() => UvalClockTaksAsync());
Task dbSavingTask = new Task(() => WriteTaskAsync(dt, final));
Console.WriteLine("start saving");
UvalClockTaksTask.Start();
await UvalClockTaksTask;
dbSavingTask.Start();
await dbSavingTask;
Console.WriteLine("saved");
Thread.Sleep(30000);
}
}
Here I'm updating timestamp for every user in dt
void UvalClockTaksAsync()
{
foreach (DataRow row in users.Rows)
{
if (Convert.ToDouble(row[uvaltimePost]) < TimeCurrent)
row[uvaltimePost] = TimeCurrent;
}
}
And here I do the saving
void WriteTaskAsync(DataTable dt, bool final)
{
try
{
dt.WriteXml(filePath + "users.xml_be");
File.Replace(#$"{filePath}users.xml_be", #$"{filePath}users.xml", #$"{filePath}users.xml_be2");
if (final)
exiting = true;
}
catch (Exception ex)
{
try
{
File.AppendAllText($"{filePath}log.txt", $"{DateTime.Now.ToString()} {ex.Message}" + Environment.NewLine);
File.AppendAllText($"{filePath}log.txt", $"{ex.StackTrace}" + Environment.NewLine);
File.AppendAllText($"{filePath}log.txt", "////////////////////////////////////////////////////////////////" + Environment.NewLine);
}
catch (Exception)
{
client.SendMessage(tChannel, ErrorMes1);
client.SendMessage(tChannel, $"{ex.Message}|{ex.StackTrace}");
}
}
}
And it still not saving every time and the weirdest thing is that files users.xml and users.xml_be are not the same! Users timestamps are always different by 10s seconds. I mean wtf
If the file has a large volume of data, it could take time to write in a physical path. So can you increase the timeline (Thread.Sleep(10000);)
Hello i created a program which reads serial data from a scoreboard, then depending on the string the program separates the data into different boxes on the form and then to different txt files.The purpose of this, is to use those txt files for livestreaming purposes in basketball games.
It's the first time i work with serial data and i am not a very experienced programmer.
My problem, as the title of this post suggests is that every now and then without any reason i am loosing some packages. This is happening randomly , for example in 10 second period i could 1 package while the next one none or 4.
private void ReadData()
{
Thread MyThread = null;
{
try
{
ThreadStart ThreadMethod = new ThreadStart(ReadFromPort);
MyThread = new Thread(ThreadMethod);
}
catch (Exception e)
{
Console.WriteLine("Failed to create thread! " + e.Message);
return;
}
try
{
MyThread.Start();
}
catch (Exception e)
{
Console.WriteLine("The thread failed to start! " + e.Message);
}
}
}
//Recieves data and write them on textbox (optionally on a txt)
private void ReadFromPort()
{
while (Receiver == true)
{
try
{
int count = ComPort.BytesToRead;
System.Windows.Forms.Application.DoEvents();
byte[] data = new byte[count];
ComPort.Read(data, 0, data.Length);
currentMessage = Combine(currentMessage, data);
ReceivedData = (BitConverter.ToString(data));
if (count > 0)
{
if (chBoxUpdate.Checked)
{
DataType = count;
tempData = ReceivedData;
this.Invoke(new EventHandler(DisplayText));
if (chboxTxt.Checked)
{
this.Invoke(new EventHandler(ExportData));
}
}
else if (chBoxPrevious.Checked)
{
DataType = count;
tempData = ReceivedData;
this.Invoke(new EventHandler(ClearData));
this.Invoke(new EventHandler(DisplayText));
if (chboxTxt.Checked)
{
this.Invoke(new EventHandler(ExportData));
}
}
}
}
catch (Exception e)
{
}
}
}
//Displays Text
private void DisplayText(object sender, EventArgs e)
{
string temp;
Console.WriteLine(tempData+ " (" + tempData.Length.ToString() + ")");
try
{
if (tempData.Length == 38)// && ReceivedData.Substring(12, 5) == "03-02")
{
if (tempData.Substring(12, 5) == "03-02")
{
DataText.AppendText(tempData.Substring(24, 8));
DataText.AppendText("\n");
Blink.Text = "Reading...";
timer1.Start();
timer1.Enabled = true;
}
}
else
if (tempData.Length == 35)
{
if (tempData.Substring(12, 5) == "45-02")
{
AttackTime.AppendText(tempData.Substring(24, 5));
Blink.Text = "Reading...";
AttackTime.AppendText("\n");
timer1.Start();
timer1.Enabled = true;
}
}
else
if (tempData.Length == 29)
{
if (tempData.Substring(12, 5) == "03-36")
{
HomeScore.AppendText(tempData.Substring(21, 2));
Blink.Text = "Reading...";
HomeScore.AppendText("\n");
timer1.Start();
timer1.Enabled = true;
}
else
if (tempData.Substring(12, 5) == "03-46")
{
AwayScore.AppendText(tempData.Substring(21, 2));
Blink.Text = "Reading...";
AwayScore.AppendText("\n");
timer1.Start();
timer1.Enabled = true;
}
}
}
catch (ArgumentOutOfRangeException)
{
}
}
Keep in mind that tempData and ReceivedData are the same here and as the programs appears now there's not any particular reason to set tempData=ReceivedData.This is a part of a different,older code i used in the begining which i never changed but it doesn't effect the program.
At first i am using a thread to enable the ReadFromPort then if the program finds that there are available data in line it displays them with the DisplayText and it's using the ExportData to export the data to a txt.I think the problem is somewhere there but as i am not very experienced i can't tell where.
Are there any suggestions on how to improve my code? If you more details or information i can provide them.
You are using Invoke, this blocks the caller thread until the event has been processed, and since you are updating the UI this can take some time. This probably causes some buffer to overflow and data to be discarded. So you should do as little work as possible on the reading thread.
There are a few alternatives
Put the data on a concurrentQueue, and let the UI thread have a timer that periodically triggers a method that reads from the queue and updates the UI.
Put the data on a concurrentQueue, wrapped in a blocking collection, have another background thread iterate over the blocking collection, using GetConsumingEnumerable, and post the result to the main thread once done. This would be suitable if there is significant processing to be done.
Use BeginInvoke to post the received data to the main-thread, this does not wait for the call to complete, but probably have slightly higher overhead that the previous alternatives. I would recommend not accessing any UI properties from the background thread, since by default no property of a UI class is threadsafe. Even if you might get away with it if you are only reading, I would not take the chance.
We have a cloud service using a worker role to process messages it receives from a Topic set up on Azure Service Bus.
The message itself seems to arrive intact and is usually received and processed correctly. In some instances however, the message seems to stop processing (Logging abruptly ends and no more references to the message being processed are seen in our WadLogsTable). From my research, this might be happening due to the worker role keeping its connection open and idle for longer than seconds. How would I go about preventing these long-to-process messages from being abandoned?
The code for our worker role is below.
public class WorkerRole : RoleEntryPoint
{
private static StandardKernel _kernel;
private readonly ManualResetEvent _completedEvent = new ManualResetEvent(false);
private BaseRepository<CallData> _callDataRepository;
private BaseRepository<CallLog> _callLogRepository;
private SubscriptionClient _client;
private NamespaceManager _nManager;
private OnMessageOptions _options;
private BaseRepository<Site> _siteRepository;
public override void Run()
{
try
{
List<CallInformation> callInfo;
Trace.WriteLine("Starting processing of messages");
// Initiates the message pump and callback is invoked for each message that is received, calling close on the client will stop the pump.
_client.OnMessage(message =>
{
// Process message from subscription.
Trace.TraceInformation("Call Received. Ready to process message ");
message.RenewLock();
callInfo = message.GetBody<List<CallInformation>>();
writeCallData(callInfo);
Trace.TraceInformation("Call Processed. Clearing from topic.");
}, _options);
}
catch (Exception e)
{
Trace.TraceInformation("Error: " + e.Message + "---" + e.StackTrace);
}
}
private void writeCallData(List<CallInformation> callList)
{
try
{
Trace.TraceInformation("Calls received: " + callList.Count);
foreach (var callInfo in callList)
{
Trace.TraceInformation("Unwrapping call...");
var call = callInfo.CallLog.Unwrap();
Trace.TraceInformation("Begin Processing: Local Call " + call.ID + " with " + callInfo.DataPoints.Length + " datapoints");
Trace.TraceInformation("Inserting Call...");
_callLogRepository.ExecuteSqlCommand(/*SNIP: Insert call*/);
Trace.TraceInformation("Call entry written. Now building datapoint list...");
var datapoints = callInfo.DataPoints.Select(datapoint => datapoint.Unwrap()).ToList();
Trace.TraceInformation("datapoint list constructed. Processing datapoints...");
foreach (var data in datapoints)
{
/*SNIP: Long running code. Insert our datapoints one at a time. Sometimes our messages die in the middle of this foreach. */
}
Trace.TraceInformation("All datapoints written for call with dependable ID " + call.Call_ID);
Trace.TraceInformation("Call Processed successfully.");
}
}
catch (Exception e)
{
Trace.TraceInformation("Call Processing Failed. " + e.Message);
}
}
public override bool OnStart()
{
try
{
var connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
_nManager = NamespaceManager.CreateFromConnectionString(connectionString);
_nManager.Settings.OperationTimeout = new TimeSpan(0,0,10,0);
var topic = new TopicDescription("MyTopic")
{
DuplicateDetectionHistoryTimeWindow = new TimeSpan(0, 0, 10, 0),
DefaultMessageTimeToLive = new TimeSpan(0, 0, 10, 0),
RequiresDuplicateDetection = true,
};
if (!_nManager.TopicExists("MyTopic"))
{
_nManager.CreateTopic(topic);
}
if (!_nManager.SubscriptionExists("MyTopic", "AllMessages"))
{
_nManager.CreateSubscription("MyTopic", "AllMessages");
}
_client = SubscriptionClient.CreateFromConnectionString(connectionString, "MyTopic", "AllMessages",
ReceiveMode.ReceiveAndDelete);
_options = new OnMessageOptions
{
AutoRenewTimeout = TimeSpan.FromMinutes(5),
};
_options.ExceptionReceived += LogErrors;
CreateKernel();
_callLogRepository.ExecuteSqlCommand(/*SNIP: Background processing*/);
}
catch (Exception e)
{
Trace.TraceInformation("Error on roleStart:" + e.Message + "---" + e.StackTrace);
}
return base.OnStart();
}
public override void OnStop()
{
// Close the connection to Service Bus Queue
_client.Close();
_completedEvent.Set();
}
void LogErrors(object sender, ExceptionReceivedEventArgs e)
{
if (e.Exception != null)
{
Trace.TraceInformation("Error: " + e.Exception.Message + "---" + e.Exception.StackTrace);
_client.Close();
}
}
public IKernel CreateKernel()
{
_kernel = new StandardKernel();
/*SNIP: Bind NInjectable repositories */
return _kernel;
}
}
Your Run method does not go on indefinitely. It should look like this:
public override void Run()
{
try
{
Trace.WriteLine("WorkerRole entrypoint called", "Information");
while (true)
{
// Add code here that runs in the role instance
}
}
catch (Exception e)
{
Trace.WriteLine("Exception during Run: " + e.ToString());
// Take other action as needed.
}
}
Taken from the docs:
The Run is considered the Main method for your application. Overriding
the Run method is not required; the default implementation never
returns. If you do override the Run method, your code should block
indefinitely. If the Run method returns, the role is automatically
recycled by raising the Stopping event and calling the OnStop method
so that your shutdown sequences may be executed before the role is
taken offline.
TheDude's response is very close to the correct answer! It turns out he's right that the run method needs to stay alive instead of returning immediately. With Azure Service Bus's message pump mechanism though, you can't place the _client.onMessage(...) inside a while loop, as this results in an error (The message pump has already been initialized).
What actually needs to happen is a a manual reset event needs to be created before the worker role begins executing, and then waited after the message pump code is executed. For documentation on ManualResetEvent, see https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=vs.110).aspx. Additionally, the process is described here: http://www.acousticguitar.pro/questions/607359/using-queueclient-onmessage-in-an-azure-worker-role
My final worker role class looks like this:
public class WorkerRole : RoleEntryPoint
{
private static StandardKernel _kernel;
private readonly ManualResetEvent _completedEvent = new ManualResetEvent(false);
private BaseRepository<CallLog> _callLogRepository;
private SubscriptionClient _client;
private MessagingFactory _mFact;
private NamespaceManager _nManager;
private OnMessageOptions _options;
public override void Run()
{
ManualResetEvent CompletedEvent = new ManualResetEvent(false);
try
{
CallInformation callInfo;
// Initiates the message pump and callback is invoked for each message that is received, calling close on the client will stop the pump.
_client.OnMessage(message =>
{
// Process message from subscription.
Trace.TraceInformation("Call Received. Ready to process message " + message.MessageId);
callInfo = message.GetBody<CallInformation>();
WriteCallData(callInfo);
Trace.TraceInformation("Call Processed. Clearing from topic.");
}, _options);
}
catch (Exception e)
{
Trace.TraceInformation("Error: " + e.Message + "---" + e.StackTrace);
}
CompletedEvent.WaitOne();
}
private void writeCallData(List<CallInformation> callList)
{
try
{
Trace.TraceInformation("Calls received: " + callList.Count);
foreach (var callInfo in callList)
{
Trace.TraceInformation("Unwrapping call...");
var call = callInfo.CallLog.Unwrap();
Trace.TraceInformation("Begin Processing: Local Call " + call.ID + " with " + callInfo.DataPoints.Length + " datapoints");
Trace.TraceInformation("Inserting Call...");
_callLogRepository.ExecuteSqlCommand(/*SNIP: Insert call*/);
Trace.TraceInformation("Call entry written. Now building datapoint list...");
var datapoints = callInfo.DataPoints.Select(datapoint => datapoint.Unwrap()).ToList();
Trace.TraceInformation("datapoint list constructed. Processing datapoints...");
foreach (var data in datapoints)
{
/*SNIP: Long running code. Insert our datapoints one at a time. Sometimes our messages die in the middle of this foreach. */
}
Trace.TraceInformation("All datapoints written for call with dependable ID " + call.Call_ID);
Trace.TraceInformation("Call Processed successfully.");
}
}
catch (Exception e)
{
Trace.TraceInformation("Call Processing Failed. " + e.Message);
}
}
public override bool OnStart()
{
try
{
var connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
_nManager = NamespaceManager.CreateFromConnectionString(connectionString);
_nManager.Settings.OperationTimeout = new TimeSpan(0,0,10,0);
var topic = new TopicDescription("MyTopic")
{
DuplicateDetectionHistoryTimeWindow = new TimeSpan(0, 0, 10, 0),
DefaultMessageTimeToLive = new TimeSpan(0, 0, 10, 0),
RequiresDuplicateDetection = true,
};
if (!_nManager.TopicExists("MyTopic"))
{
_nManager.CreateTopic(topic);
}
if (!_nManager.SubscriptionExists("MyTopic", "AllMessages"))
{
_nManager.CreateSubscription("MyTopic", "AllMessages");
}
_client = SubscriptionClient.CreateFromConnectionString(connectionString, "MyTopic", "AllMessages",
ReceiveMode.ReceiveAndDelete);
_options = new OnMessageOptions
{
AutoRenewTimeout = TimeSpan.FromMinutes(5),
};
_options.ExceptionReceived += LogErrors;
CreateKernel();
_callLogRepository.ExecuteSqlCommand(/*SNIP: Background processing*/);
}
catch (Exception e)
{
Trace.TraceInformation("Error on roleStart:" + e.Message + "---" + e.StackTrace);
}
return base.OnStart();
}
public override void OnStop()
{
// Close the connection to Service Bus Queue
_client.Close();
_completedEvent.Set();
}
void LogErrors(object sender, ExceptionReceivedEventArgs e)
{
if (e.Exception != null)
{
Trace.TraceInformation("Error: " + e.Exception.Message + "---" + e.Exception.StackTrace);
_client.Close();
}
}
public IKernel CreateKernel()
{
_kernel = new StandardKernel();
/*SNIP: Bind NInjectable repositories */
return _kernel;
}
}
You'll notice the presence of the ManualResetEvent and the invocation of WaitOne() at the end of my Run method. I hope someone finds this helpful!
I have several services and use threads to implement each one. The services are executed sequentially. Let me just pick up one for demo purpose.
services[1] = new RProcessing() { ConsoleInfoColor = ConsoleColor.Cyan };
success = services[1].Start();
if (success)
{
OutputUtils.WriteLogInfo("Service " + services[1].Name + " started...");
}
else
{
OutputUtils.WriteLogInfo("Service " + services[1].Name + " failed to start...");
previousStartup = false;
services[0].Stop();
}
Inside RProcessing.
public RProcessing()
{
worker = new Thread[1]; // Only one thread
for (int i = 0; i < 1; i++)
{
worker[i] = new Thread(new ThreadStart(ServiceLoop));
worker[i].Name = "R Thread_" + i.ToString();
}
// processing
}
public bool Start()
{
foreach (Thread t in worker)
t.Start();
return (true);
}
public bool Stop()
{
if (_isRunning)
{
_isRunning = false;
}
_isRunning = false;
base.Dispose();
WriteLogInfo("Shutdown of R Processor complete");
return (true);
}
public void ServiceLoop()
{
_isRunning = true;
WriteLogInfo("Starting ServiceLoop() for: " + Assembly.GetAssembly(typeof(RProcessing)).FullName);
string s;
while (_isRunning)
{
Thread.Sleep(500);
s = null;
try
{
WriteLogInfo(" processing "+s);
Thread.Sleep(864);// 24 hours.
}
catch (Exception ex)
{
WriteLogError("Thread " + Thread.CurrentThread.Name + " " + ex.ToString());
}
}
if (this._isRunning)
{
WriteLogInfo("Restarting thread due to failure...");
try
{
Thread.CurrentThread.Start();
}
catch (Exception ex)
{
WriteLogError("Error restarting thread... " + ex.ToString());
}
}
}
Only there is one thread, I want to finish it then go back the next service. However it is always inside the ServiceLoop. How can break it? Just call Stop()?
Well, comment is misleading. Thread will sleep for 864 milliseconds, and not 24 hours.
Thread.Sleep(864);// 24 hours.
If you really intend to sleep that long in a loop, then use ManualResetEvent so that you could abort the wait at any time.
ManualResetEvent cancelEvent;
in loop:
if(cancelEvent.WaitOne(TimeSpan.FromHours(24))){
break;
}
and, in Stop method:
cancelEvent.Set();
Also remove that:
if (this._isRunning)
{
WriteLogInfo("Restarting thread due to failure...");
try
{
Thread.CurrentThread.Start();
}
catch (Exception ex)
{
WriteLogError("Error restarting thread... " + ex.ToString());
}
}
And make sure that _isRunning is volatile, otherwise it may be cached and not updated in another thread. Your service will exit peacefully when you call Stop().