C# Ping-Class - cancel Ping.Send() if connection error - c#

i built a simple program that takes an excel list containing a set of server host names and pinging each one of these returning the results in the console how many of the hosts are online.
I am using a try/catch block to prevent the app from crashing when the hostname is unknown (upon connection error).
Now i am looking for a solution to skip the ping request upon connection error, so the program continues to ping the next hostname in line and the program can finish faster without waiting for the exception to happen.
Thats the code i am using right now.
foreach (var server in serverList)
{
try
{
var reply = pingSender.Send(server, 1000);
//if server is reachable
if (reply.Status == IPStatus.Success)
{
//add server to new excel and display it as online
onlineList.Cells[serverCount, 1] = server + " is online";
//increase counters
serverCount++;
onlineCount++;
}
else
{
//add server to new excel and display it as offline
onlineList.Cells[serverCount, 2] = server + " is offline or unreachable.";
serverCount++;
}
}
catch (Exception)
{
//if in any case the destination is unknown and prevent application from crashing
onlineList.Cells[serverCount, 2] = server + " is offline or unreachable.";
serverCount++;
}
}

The problem is that you perform the ping sequentially, one after the other. Improve performance by having multiple threads run in parallel to perform the pinging like in this sample code.
private async void button1_Click(object sender, EventArgs e)
{
// Make a list of host names to ping
List<string> hostnames = new List<string>();
// Use local hostnames a .. z.
// and domain names a.com .. z.com
for(char c = 'a'; c <= 'z'; c++)
{
hostnames.Add(c.ToString());
hostnames.Add("www." + c + ".com");
}
// Use some well known hostnames
hostnames.Add("www.google.com");
hostnames.Add("www.microsoft.com");
hostnames.Add("www.apple.com");
// Ping the hostnames and get the results as a dictionary
IReadOnlyDictionary<string, bool> results = await Task.Run(() => PingHosts(hostnames));
// Output the content of the results, note that the order
// is not the same as in the original list of
// hostnames because of parallel processing
foreach(var result in results)
{
if(result.Value)
{
textBox1.AppendText("Ping to " + result.Key + ": OK" + Environment.NewLine);
}
else
{
textBox1.AppendText("Ping to " + result.Key + ": ERROR" + Environment.NewLine);
}
}
}
// This function will ping a single host and return true or false
// based on whether it can be pinged
private bool PingSingleHost(string hostname)
{
try
{
Ping ping = new Ping();
var reply = ping.Send(hostname, 1000);
bool wasSuccessful = reply.Status == IPStatus.Success;
return wasSuccessful;
}
catch(Exception)
{
return false;
}
}
// Ping the given list of hostnames and return their status
// (can they be pinged) as a dictionary where the Key is the hostname
// and the value is a boolean with the ping result
private IReadOnlyDictionary<string, bool> PingHosts(IEnumerable<string> hostnames)
{
// Use 30 parallel worker threads
// This number can be quite high because the
// workers will spend most time waiting
// for an answer
int numberOfWorkers = 30;
// Place the hosts into a queue
ConcurrentQueue<string> jobsQueue = new ConcurrentQueue<string>(hostnames);
// Use a concurrent dictionary to store the results
// The concurrent dictionary will automatically handle
// multiple threads accessing the dictionary at the same time
ConcurrentDictionary<string, bool> results = new ConcurrentDictionary<string, bool>();
// Worker function which keeps reading new hostnames from the given jobsQueue
// and pings those hostnames and writes the result to the given results dictionary
// until the queue is empty
ThreadStart Worker = () =>
{
string hostname;
// While the job queue is not empty, get a host from the queue
while(jobsQueue.TryDequeue(out hostname))
{
// Ping the host and
bool wasSuccessful = PingSingleHost(hostname);
// write the result to results dictionary
results.TryAdd(hostname, wasSuccessful);
}
};
// Start the Worker threads
List<Thread> workers = new List<Thread>();
for(int i = 0; i < numberOfWorkers; i++)
{
Thread thread = new Thread(Worker);
thread.Start();
workers.Add(thread);
}
// Wait for all workers to have finished
foreach(var thread in workers)
{
thread.Join();
}
return results;
}

Related

How to fix high CPU usage found using performance profiling results?

Intro
We are building a c# data collector that collects tagged data from an OPC source (data being sampled every 1/2 second. The code runs well except for the fact that it uses around 25% of the CPU (XEON 2.8GHz 8GB). Using the Visual Studio Performance Profiler, we got the following :
Overall CPU usage
We identified 2 functions with high CPU consumption : Hot path leading to the 2 high consumption functions
We are noticing that the function, itself, doesn't consume much CPU ressources (Self CPU around 1.5%) but around 50% of the whole app consumption (Total CPU around 50%).
Digging further into the "PrepareTags() function, we found that SemaphoreSlim.Release() and Wait() methods are mostly responsible for the CPU usage : PrepareTags() function consumption
Global code structure
When the program begins, the 3 main threads are started. The one I'm focussing on here is the REST API thread. These threads are "permanent" and kept active as long as the application runs. Also, each main thread will start new threads, some are permanent while others will execute and stop.
Based on my observations, around 40 threads are simultaneously active with small variations caused by threads being started and stopped. Thread number is kept constant so it seems there is no deadlocks.
3 Main threads are starting
public void StartThreads()
{
WriteLog.WriteFollowLog("CollecterService : is Started");
//set up default culture
CultureInfo culture = CultureInfo.CreateSpecificCulture(ConfigurationManager.AppSettings["Culturesetting"]);
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
listOfTagUpdated = new SemaphoreSlim(0);
listOfTag = new Dictionary<string, TagSourceAddressObject>();
tcpMemory = new TagMemoryFinFout();
tagAdressMemoryForStat = new TagAdressMemoryFinFout();
tagMemory = new TagMemoryLastValue(false);
//configuration
try
{
// Creation of the Stat thread
this.statThread = new ThreadForCalculation(listOfTag, tagAdressMemoryForStat, tagMemory, tcpMemory);
// Creation of the REST API client thread
this.restApiThread = new ThreadRestApiClient(listOfTag, tcpMemory, listOfTagUpdated, tagMemory);
// Creation of the OPCDA Thread
this.opcDaThread = new ThreadOpcDaClient(listOfTag, tcpMemory, tagAdressMemoryForStat, tagMemory, listOfTagUpdated);
if (configuration != null)
{
this.configuration.StartTracking(this.restApiThread);
}
}
catch (Exception ex)
{
WriteLog.WriteErrorLog(ex);
}
}
The RestAPIThreadClient will also start 3 threads including the permanent "PrepareTags" thread.
RestAPIThread permanent thread starts 3 more permanent threads
...
// Start the authentication and the websocket handlers for this connection
this.AuthenticationConnectionThread = new Thread(CheckConnectionAuthentication);
this.AuthenticationConnectionThread.Name = "AuthentificationConnectionThread";
this.AuthenticationConnectionThread.Start();
this.WebsocketConnectionThread = new Thread(CheckWebsocketConnection);
this.WebsocketConnectionThread.Name = "WebsocketConnectionThread";
this.WebsocketConnectionThread.Start();
this.TcpPrepareTagThread = new Thread(PrepareTags);
this.TcpPrepareTagThread.Name = "TcpPrepareThread";
this.TcpPrepareTagThread.Start();
...
For better comprehension here is what a "TagValueObject" looks like :
TagValueObject newTag = new TagValueObject(null);
newTag.TagID = tag.TagID;
newTag.TagQuality = tag.TagQuality;
newTag.TagTimeStamp = tag.TagTimeStamp;
newTag.TagValue = tag.TagValue;
newTag.TagName = tag.TagName;
The overall process is :
OPC server sends tag data to our collector (every 1/2 second)
The collector stores these in memory
After some processing they are redirected to an API on AWS
The high CPU usage is observed in step 3
What happens in step 3
1. PrepareTags() gets called.
Function PrepareTags()
private void PrepareTags()
{
WriteLog.WriteFollowLog("TCPRestAPIClient : Is Started");
while (!stopWorking)
{
if (token != null)
{
TagValueObject tag = tcpMemory.GetFirstTag();
if (tag != null)
{
tagsToSend.Add(tag);
int numOfTagsToReturn = 50;
if ((readyToSend == true && tagsToSend.Count > 0) || tagsToSend.Count >= numOfTagsToReturn)
{
readyToSend = false;
//WriteLog.WriteFollowLog("Start count:" + tagsToSend.Count.ToString());
List<TagValueObject> tags = tagsToSend.GetRange(0, tagsToSend.Count > numOfTagsToReturn ? numOfTagsToReturn : tagsToSend.Count);
tagsToSend.RemoveRange(0, tagsToSend.Count > numOfTagsToReturn ? numOfTagsToReturn : tagsToSend.Count);
//WriteLog.WriteFollowLog("End count:" + tagsToSend.Count.ToString() + " / Remaining: " + tcpMemory.TagMemorySlot.Count);
Thread thread = new Thread(() =>
{
ProcessTagRequest(tags);
});
thread.Name = "PrepareTags";
thread.Start();
}
}
}
}
}
2. We get a range of tags stored in memory through "GetFirstTag()" and start the ProcessTagRequest Thread
Function GetFirsTag()
public TagValueObject GetFirstTag()
{
TagValueObject tag;
if (valueUpdated.Wait(10))
{
//WriteLog.WriteFollowLog(String.Format("TagMemoryFinFout-GetFirstTag Semaphore(valueUpdated) : Entered. Available Slots = {0}", valueUpdated.CurrentCount));
lock (thislock)
{
tag = tagMemorySlot.FirstOrDefault();
tagMemorySlot.Remove(tag);
}
valueUpdated.Release();
//WriteLog.WriteFollowLog(String.Format("TagMemoryFinFout-GetFirstTag Semaphore(valueUpdated) : Released. Available Slots = {0}", valueUpdated.CurrentCount));
}
else
{
tag = null;
}
return tag;
}
3. "ProcessTagRequest()" thread is started
Function ProcessTagRequest()
private void ProcessTagRequest(List<TagValueObject> tags)
{
if (tags.Count == 0) return;
try
{
sendData(tags, false);
}
catch (Exception ex)
{
WriteLog.WriteErrorLog("TCPRestAPIClient", ex);
}
//WriteLog.WriteErrorLog("Closed Thread");
}
4. We finally send the data to the API through sendData() function.
Function sendData()
private void sendData(List<TagValueObject> valueArray, Boolean externalMemory)
{
if (valueArray.Count > 0)
{
List<object> valueArrayAdjusted = new List<object>();
foreach (var valueEntry in valueArray){
dynamic data = new ExpandoObject();
data.value = valueEntry.TagValue;
data.date = valueEntry.TagTimeStamp;
data.quality = valueEntry.TagQuality;
data._id = valueEntry.TagID;
valueArrayAdjusted.Add(data);
}
var body = new
{
tags = valueArrayAdjusted
};
Boolean sendFail = true;
var tries = 0;
do
{
tries++;
IRestResponse response = APICall(Method.POST, "/senddata", body, token);
if (response.StatusCode == HttpStatusCode.OK)
{
sendFail = false;
// WriteLog.WriteErrorLog("Senddata connected successfully : " + response.StatusCode + " " + response.Content);
}
else
{
WriteLog.WriteFollowLog("Senddata failed to connect : " + response.StatusCode + " " + response.Content);
}
} while (tries < 1 && sendFail);
if (sendFail)
{
try
{
if (ConfigurationManager.AppSettings["UseExternalStorage"] == "true")
{
foreach (TagValueObject value in valueArray)
{
StoreInExternalMemory(value);
}
WriteLog.WriteFollowLog(String.Format("Senddata : Stored {0} tags in external memory", valueArray.Count));
}
} catch (Exception e)
{
WriteLog.WriteErrorLog("ThreatRestApiClient : " + e.ToString());
}
}
}
}
My question is : How to reduce the CPU usage? What is wrong in this code, architecture?

Download multiple files concurrently from FTP using FluentFTP with a maximum value

I would like to download multiple download files recursively from a FTP Directory, to do this I'm using FluentFTP library and my code is this one:
private async Task downloadRecursively(string src, string dest, FtpClient ftp)
{
foreach(var item in ftp.GetListing(src))
{
if (item.Type == FtpFileSystemObjectType.Directory)
{
if (item.Size != 0)
{
System.IO.Directory.CreateDirectory(Path.Combine(dest, item.Name));
downloadRecursively(Path.Combine(src, item.Name), Path.Combine(dest, item.Name), ftp);
}
}
else if (item.Type == FtpFileSystemObjectType.File)
{
await ftp.DownloadFileAsync(Path.Combine(dest, item.Name), Path.Combine(src, item.Name));
}
}
}
I know you need one FtpClient per download you want, but how can I make to use a certain number of connections as maximum, I guess that the idea is to create, connect, download and close per every file I find but just having a X number of downloading files at the same time. Also I'm not sure if I should create Task with async, Threads and my biggest problem, how to implement all of this.
Answer from #Bradley here seems pretty good, but the question does read every file thas has to download from an external file and it doesn't have a maximum concurrent download value so I'm not sure how to apply these both requirements.
Use:
ConcurrentBag class to implement a connection pool;
Parallel class to parallelize the operation;
ParallelOptions.MaxDegreeOfParallelism to limit number of the concurrent threads.
var clients = new ConcurrentBag<FtpClient>();
var opts = new ParallelOptions { MaxDegreeOfParallelism = maxConnections };
Parallel.ForEach(files, opts, file =>
{
file = Path.GetFileName(file);
string thread = $"Thread {Thread.CurrentThread.ManagedThreadId}";
if (!clients.TryTake(out var client))
{
Console.WriteLine($"{thread} Opening connection...");
client = new FtpClient(host, user, pass);
client.Connect();
Console.WriteLine($"{thread} Opened connection {client.GetHashCode()}.");
}
string remotePath = sourcePath + "/" + file;
string localPath = Path.Combine(destPath, file);
string desc =
$"{thread}, Connection {client.GetHashCode()}, " +
$"File {remotePath} => {localPath}";
Console.WriteLine($"{desc} - Starting...");
client.DownloadFile(localPath, remotePath);
Console.WriteLine($"{desc} - Done.");
clients.Add(client);
});
Console.WriteLine($"Closing {clients.Count} connections");
foreach (var client in clients)
{
Console.WriteLine($"Closing connection {client.GetHashCode()}");
client.Dispose();
}
Another approach is to start a fixed number of threads with one connection for each and have them pick files from a queue.
For an example of an implementation, see my article for WinSCP .NET assembly:
Automating transfers in parallel connections over SFTP/FTP protocol
A similar question about SFTP:
Processing SFTP files using C# Parallel.ForEach loop not processing downloads
Here is a TPL Dataflow approach. A BufferBlock<FtpClient> is used as a pool of FtpClient objects. The recursive enumeration takes a parameter of type IEnumerable<string> that holds the segments of one filepath. These segments are combined differently when constructing the local and the remote filepath. As a side effect of invoking the recursive enumeration, the paths of the remote files are sent to an ActionBlock<IEnumerable<string>>. This block handles the parallel downloading of the files. Its Completion property contains eventually all the exceptions that may have occurred during the whole operation.
public static Task FtpDownloadDeep(string ftpHost, string ftpRoot,
string targetDirectory, string username = null, string password = null,
int maximumConnections = 1)
{
// Arguments validation omitted
if (!Directory.Exists(targetDirectory))
throw new DirectoryNotFoundException(targetDirectory);
var fsLocker = new object();
var ftpClientPool = new BufferBlock<FtpClient>();
async Task<TResult> UsingFtpAsync<TResult>(Func<FtpClient, Task<TResult>> action)
{
var client = await ftpClientPool.ReceiveAsync();
try { return await action(client); }
finally { ftpClientPool.Post(client); } // Return to the pool
}
var downloader = new ActionBlock<IEnumerable<string>>(async path =>
{
var remotePath = String.Join("/", path);
var localPath = Path.Combine(path.Prepend(targetDirectory).ToArray());
var localDir = Path.GetDirectoryName(localPath);
lock (fsLocker) Directory.CreateDirectory(localDir);
var status = await UsingFtpAsync(client =>
client.DownloadFileAsync(localPath, remotePath));
if (status == FtpStatus.Failed) throw new InvalidOperationException(
$"Download of '{remotePath}' failed.");
}, new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = maximumConnections,
BoundedCapacity = maximumConnections,
});
async Task Recurse(IEnumerable<string> path)
{
if (downloader.Completion.IsCompleted) return; // The downloader has failed
var listing = await UsingFtpAsync(client =>
client.GetListingAsync(String.Join("/", path)));
foreach (var item in listing)
{
if (item.Type == FtpFileSystemObjectType.Directory)
{
if (item.Size != 0) await Recurse(path.Append(item.Name));
}
else if (item.Type == FtpFileSystemObjectType.File)
{
var accepted = await downloader.SendAsync(path.Append(item.Name));
if (!accepted) break; // The downloader has failed
}
}
}
// Move on to the thread pool, to avoid ConfigureAwait(false) everywhere
return Task.Run(async () =>
{
// Fill the FtpClient pool
for (int i = 0; i < maximumConnections; i++)
{
var client = new FtpClient(ftpHost);
if (username != null && password != null)
client.Credentials = new NetworkCredential(username, password);
ftpClientPool.Post(client);
}
try
{
// Enumerate the files to download
await Recurse(new[] { ftpRoot });
downloader.Complete();
}
catch (Exception ex) { ((IDataflowBlock)downloader).Fault(ex); }
try
{
// Await the downloader to complete
await downloader.Completion;
}
catch (OperationCanceledException)
when (downloader.Completion.IsCanceled) { throw; }
catch { downloader.Completion.Wait(); } // Propagate AggregateException
finally
{
// Clean up
if (ftpClientPool.TryReceiveAll(out var clients))
foreach (var client in clients) client.Dispose();
}
});
}
Usage example:
await FtpDownloadDeep("ftp://ftp.test.com", "", #"C:\FtpTest",
"username", "password", maximumConnections: 10);
Note: The above implementation enumerates the remote directory lazily, following the tempo of the downloading process. If you prefer to enumerate it eagerly, gathering all info available about the remote listings ASAP, just remove the BoundedCapacity = maximumConnections configuration from the ActionBlock that downloads the files. Be aware that doing so could result in high memory consumption, in case the remote directory has a deep hierarchy of subfolders, containing cumulatively a huge number of small files.
I'd split this into three parts.
Recursively build a list of source and destination pairs.
Create the directories required.
Concurrently download the files.
It's the last part that is slow and should be done in parallel.
Here's the code:
private async Task DownloadRecursively(string src, string dest, FtpClient ftp)
{
/* 1 */
IEnumerable<(string source, string destination)> Recurse(string s, string d)
{
foreach (var item in ftp.GetListing(s))
{
if (item.Type == FtpFileSystemObjectType.Directory)
{
if (item.Size != 0)
{
foreach(var pair in Recurse(Path.Combine(s, item.Name), Path.Combine(d, item.Name)))
{
yield return pair;
}
}
}
else if (item.Type == FtpFileSystemObjectType.File)
{
yield return (Path.Combine(s, item.Name), Path.Combine(d, item.Name));
}
}
}
var pairs = Recurse(src, dest).ToArray();
/* 2 */
foreach (var d in pairs.Select(x => x.destination).Distinct())
{
System.IO.Directory.CreateDirectory(d);
}
/* 3 */
var downloads =
pairs
.AsParallel()
.Select(x => ftp.DownloadFileAsync(x.source, x.destination))
.ToArray();
await Task.WhenAll(downloads);
}
It should be clean, neat, and easy to reason about code.

NetMQ: Can NetMQPoller take care of async message sending?

Does anyone have an example of using a NetMQPoller in conjunction with sending messages from an asynchronous task? i.e.
// 1. Receive a message from one of many clients
// 2. Handle client requests on multiple async Task's (may be file bound)
// 3. Respond to clients when long running Task finishes
We are currently using a very barebones Dealer/Router setup for multiple clients to talk to a single server. On the server we pass each client request on to a pool of Tasks/Threads that handle the request, generate a response, and then attempt to respond to the original client. Because I'm new to this I didn't realize calling the client on a different Task/Thread would bork things but it makes sense now.
What I'm looking for is the right way to handle this. I could definitely have a BlockingCollection of messages to send back and just service that from the original thread, but I recently discovered the NetMQPoller and it sort of sounds like it has some of this built in. I am not 100% sure if it can help me send messages on the original thread or not but there are a lot of parts to it that sound promising. Can anyone point me at an example of sending messages back to the Dealer/client from the Router/server on a different thread than the original message was received on?
UPDATE
I think I may have sort-of solved my own question by combining
Router-Dealer example with the
WithPoller unit test from NetMQUnitTests.cs
class Program
{
static void Main( string[] args )
{
// NOTES
// 1. Use ThreadLocal<DealerSocket> where each thread has
// its own client DealerSocket to talk to server
// 2. Each thread can send using it own socket
// 3. Each thread socket is added to poller
const int delay = 3000; // millis
var clientSocketPerThread = new ThreadLocal<DealerSocket>();
using( var server = new RouterSocket("#tcp://127.0.0.1:5556") )
using( var queue = new NetMQQueue<MyResponse>() )
using( var poller = new NetMQPoller { queue } )
{
// Start some threads, each with its own DealerSocket
// to talk to the server socket. Creates lots of sockets,
// but no nasty race conditions no shared state, each
// thread has its own socket, happy days.
for( int i = 0; i < 3; i++ )
{
Task.Factory.StartNew(state =>
{
DealerSocket client = null;
if( !clientSocketPerThread.IsValueCreated )
{
client = new DealerSocket();
client.Options.Identity =
Encoding.Unicode.GetBytes(state.ToString());
client.Connect("tcp://127.0.0.1:5556");
client.ReceiveReady += Client_ReceiveReady;
clientSocketPerThread.Value = client;
poller.Add(client);
}
else
{
client = clientSocketPerThread.Value;
}
while( true )
{
var messageToServer = new NetMQMessage();
messageToServer.AppendEmptyFrame();
messageToServer.Append(state.ToString());
Console.WriteLine("======================================");
Console.WriteLine(" OUTGOING MESSAGE TO SERVER ");
Console.WriteLine("======================================");
PrintFrames("Client Sending", messageToServer);
client.SendMultipartMessage(messageToServer);
Thread.Sleep(delay);
}
}, string.Format("client {0}", i), TaskCreationOptions.LongRunning);
}
queue.ReceiveReady += ( sender, e ) =>
{
var queueItem = e.Queue.Dequeue();
var messageToClient = new NetMQMessage();
messageToClient.Append(queueItem.ClientId);
messageToClient.AppendEmptyFrame();
messageToClient.Append(queueItem.MessageToClient);
server.SendMultipartMessage(messageToClient);
};
// start the poller
poller.RunAsync();
// server loop
while( true )
{
var clientMessage = server.ReceiveMultipartMessage();
Console.WriteLine("======================================");
Console.WriteLine(" INCOMING CLIENT MESSAGE FROM CLIENT ");
Console.WriteLine("======================================");
PrintFrames("Server receiving", clientMessage);
if( clientMessage.FrameCount == 3 )
{
var clientAddress = clientMessage[0];
var clientOriginalMessage = clientMessage[2].ConvertToString();
string response = string.Format("{0} back from server {1}",
clientOriginalMessage, DateTime.Now.ToLongTimeString());
Task.Factory.StartNew(async() =>
{
await Task.Delay(200);
var clientResponse = new MyResponse()
{
ClientId = clientAddress,
MessageToClient = response
};
queue.Enqueue(clientResponse);
}, TaskCreationOptions.LongRunning);
}
}
}
}
static void PrintFrames( string operationType, NetMQMessage message )
{
for( int i = 0; i < message.FrameCount; i++ )
{
Console.WriteLine("{0} Socket : Frame[{1}] = {2}", operationType, i,
message[i].ConvertToString());
}
}
static void Client_ReceiveReady( object sender, NetMQSocketEventArgs e )
{
bool hasmore = false;
e.Socket.ReceiveFrameString(out hasmore);
if( hasmore )
{
string result = e.Socket.ReceiveFrameString(out hasmore);
Console.WriteLine("REPLY {0}", result);
}
}
class MyResponse
{
public NetMQFrame ClientId;
public string MessageToClient;
}
}

Task does not always return upon completion

So I am trying to build a program to control a machine. Communications with said machine is via a serial port for which I have written a driver. Continuous polling to the machine is necessary for status feedback etc. In my program I have a dedicated ExecutionEngine() class to handle serial send and receive. I also need to have two separate control sequences running, which I have put into methods RunSequenceA() and RunSequenceB() respectively. During normal operation, all three methods need to run until both control sequences finish, at which point the StopSequence() method is called. My issue is that sometimes, for whatever reason, the StopSequence() method is never called, leaving my ExecutionEngine() method in an infinite loop!
Code for ExecutionEngine():
private static void ExecutionEngine()
{
// Clear both lists in case they have old data
_commandList.Clear();
_pollingList.Clear();
// Poll while user has not yet clicked "STOP"
while (!_cTokenSource.Token.IsCancellationRequested)
{
// If there are commands to be sent, send them first
if (_commandList.Count > 0)
{
Command[] tempCommandArray;
lock (_commandList)
tempCommandArray = _commandList.ToArray();
foreach (var c in tempCommandArray)
{
if (_cTokenSource.Token.IsCancellationRequested)
break;
var response = SerialDriver.ComCycle(c.CommandBytes, _serialPort);
var success = CheckErrorReturn(response, false);
if (success)
{
AddPolling(c);
RemoveCommand(c);
}
}
}
// Do polling operation on applicable controllers
if (_pollingList.Count > 0)
{
Command[] tempPollingArray;
lock (_pollingList)
tempPollingArray = _pollingList.ToArray();
foreach (var c in tempPollingArray)
{
if (_cTokenSource.Token.IsCancellationRequested)
break;
var response = SerialDriver.ComCycle(c.PollBytes, _serialPort);
var success = ProcessPollReturn(response);
if (success)
{
c.FlagDone();
RemovePolling(c);
}
}
}
if (_commandList.Count + _pollingList.Count == 0)
{
// Will get stuck here if neither list gets new items added
Console.WriteLine("Bad place");
Thread.Sleep(500);
}
}
// Cancellation has been requested
lock (_commandList)
_commandList.Clear();
lock (_pollingList)
_pollingList.Clear();
ResetTriggers();
var endCommand = new Command("GL_SYSCMD", 0);
SerialDriver.ComCycle(endCommand.CommandBytes, _serialPort);
_serialPort.Close();
_vm.SequenceRunning = false;
return;
}
Code for running sequences:
private static async Task RunSequencesAsync()
{
var taskArray = new Task[2];
var a = new Action(RunSequenceA);
var b = new Action(RunSequenceB);
taskArray[0] = Task.Run(a);
taskArray[1] = Task.Run(b);
await Task.WhenAll(taskArray).ConfigureAwait(continueOnCapturedContext: false);
// Sometimes this never fires, WHY?
UpdateStatus("All done!");
StopSequence();
}
// Run A sequence
internal static void RunSequenceA()
{
if (_sequenceA1 != null && _sequenceA1.Count > 0)
{
foreach (var s in _sequenceA1)
{
if (_cTokenSource.Token.IsCancellationRequested)
return;
s.Execute();
if (s.Reference != null && TriggerStepCompleted != null)
TriggerStepCompleted(s, EventArgs.Empty);
}
// This part always fires
Console.WriteLine("Sequence A finished");
return;
}
else
return;
}
And finally, the methods to start and stop everything:
private static async Task StartSequenceAsync()
{
_serialPort.PortName = _vm.SelectedComPort;
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
// Start
_cTokenSource = new CancellationTokenSource();
_vm.SequenceRunning = true;
var taskArray = new Task[2];
taskArray[0] = Task.Run(() => ExecutionEngine());
Thread.Sleep(50);
taskArray[1] = Task.Run(() => RunSequencesAsync());
await Task.WhenAll(taskArray).ConfigureAwait(continueOnCapturedContext: false);
}
private static void StopSequence()
{
_cTokenSource.Cancel();
}
To reiterate, the problem doesn't happen every time. In fact, most times the program runs fine. It seems that problems only arise if I manually call the StopSequence() method half way through execution. Then it's 50/50 as to whether the problem shows up. I'm pretty sure my issue is threading related, but not sure exactly what is going wrong. Any help pointing me in the right direction will be greatly appreciated!

Not reach the code as expected

I have a telephony application, in which I want to invoke simultaneous calls,. Each call will occupy a channel or port. So I added all channels to a BlockingCollection. The application is a windows service.
Let's see the code.
public static BlockingCollection<Tuple<ChannelResource, string>> bc = new BlockingCollection<Tuple<ChannelResource, string>>();
public static List<string> list = new List<string>();// then add 100 test items to it.
The main application has the code:
while (true)
{
ThreadEvent.WaitOne(waitingTime, false);
lock (SyncVar)
{
Console.WriteLine("Block begin");
for (int i = 0; i < ports; i++)
{
var firstItem = list.FirstOrDefault();
if (bc.Count >= ports)
bc.CompleteAdding();
else
{
ChannelResource cr = OvrTelephonyServer.GetChannel();
bc.TryAdd(Tuple.Create(cr, firstItem));
list.Remove(firstItem);
}
}
pc.SimultaneousCall();
Console.WriteLine("Blocking end");
if (ThreadState != State.Running) break;
}
Now for the simultaneous call code:
public void SimultaneousCall()
{
Console.WriteLine("There are {0} channels to be processed.", bc.Count);
var workItemBlock = new ActionBlock<Tuple<ChannelResource, string>>(
workItem =>
{
ProcessEachChannel(workItem);
});
foreach (var workItem in bc.GetConsumingEnumerable())
{
bool result = workItemBlock.SendAsync(workItem).Result;
}
workItemBlock.Complete();
}
private void ProcessEachChannel(Tuple<ChannelResource, string> workItem)
{
ChannelResource cr = workItem.Item1;
string sipuri = workItem.Item2;
VoiceResource vr = workItem.Item1.VoiceResource;
workItem.Item1.Disconnected += new Disconnected(workItemItem1_Disconnected);
bool success = false;
try
{
Console.WriteLine("Working on {0}", sipuri);
DialResult dr = new DialResult();
// blah blah for calling....
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex.Message);
}
finally
{
if (cr != null && cr.VoiceResource != null)
{
cr.Disconnect();
cr.Dispose();
cr = null;
Console.WriteLine("Release channel for item {0}.", sipuri);
}
}
}
The question was when I tested the application with 4 ports, I thought the code should reach at
Console.WriteLine("Blocking end");
However it was not. Please see the snapshot.
The application is just hanging on after releasing the last channel. I guess that I may use the blockingcollection incorrectly. Thanks for help.
UPDATE:
Even I changed the code by using POST action as below, the situation is still unchanged.
private bool ProcessEachChannel(Tuple<ChannelResource, string> workItem)
{
// blah blah to return true or false respectively.
public void SimultaneousCall()
{
Console.WriteLine("There are {0} channels to be processed.", bc.Count);
var workItemBlock = new ActionBlock<Tuple<ChannelResource, string>>(
workItem =>
{
bool success = ProcessEachChannel(workItem);
});
foreach (var workItem in bc.GetConsumingEnumerable())
{
workItemBlock.Post(workItem);
}
workItemBlock.Complete();
}
I believe the problem is that you never call bc.CompleteAdding(): the if means it would be called in ports + 1-th iteration of the loop, but the loop iterates only ports-times. Because of this, GetConsumingEnumerable() returns a sequence that never ends, which means the foreach inside SimultaneousCall() blocks forever.
I think the right solution is to call bc.CompleteAdding() after the for loop, not in an impossible condition inside it.

Categories

Resources