I am new to C# programming.
I am trying to get the number of updates fror a list of servers using background worker. Result for every server is shown in a listview at the report progress method.
I am able to successfully get results using foreach loop, but while trying to get the same results using parallel foreach, all the columns and rows of the listview are mixed up.
for example:
output of foreach loop:
Server Name Status Updates Available
server1 Login to server failed! 0
server2 Updates are available 3
server3 Updates are available 3
server4 Up to Date 0
and so on..
output of parallel foreach:
server1 Updates are available 1
server1 Login to server failed! 1
server2 Login to server failed! 0
server3 Login to server failed! 0
server4 Login to server failed! 0
server4 Updates are available 3
and so on..
I have tried locking parts of the code and have also tried using concurrent bag but was not quite able to resolve the issue. Below is the parallelforeach code. I am doing someting wrong? Any suggestions would be of great help.
Parallel.ForEach(namelist, /*new ParallelOptions { MaxDegreeOfParallelism = 4 }, */line =>
//foreach (string line in namelist)
{
if (worker.CancellationPending)
{
e.Cancel = true;
worker.ReportProgress(SysCount, obj);
}
else
{
this.SystemName = line;//file.ReadLine();
Status.sVariables result = new Status.sVariables();
result = OneSystem(this.SystemName);
switch (result.BGWResult)
{
case -1:
this.StatusString = "Login to server failed!";
break;
//other status are assigned here;
}
SysCount++;
bag.Add(this);
}
Status returnobj;
bag.TryTake(out returnobj);
worker.ReportProgress(SysCount, returnobj);
Thread.Sleep(200);
});
ReportProgress Method:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (!backgroundWorker1.CancellationPending)
{
Status result = (Status)e.UserState;
Complete_label.Visible = true;
if (listView1.InvokeRequired)
listView1.Invoke(new MethodInvoker(delegate
{
listView1.Items.Add("");
listView1.Items[result.SysCount - 1].SubItems.Add(result.SystemName);
listView1.Items[result.SysCount - 1].SubItems.Add(result.StatusString);
listView1.Items[result.SysCount - 1].SubItems.Add(result.AvailableUpdatesCount.ToString());
}));
else
{
try
{
listView1.Items.Add("");
listView1.Items[result.SysCount - 1].SubItems.Add(result.SystemName);
listView1.Items[result.SysCount - 1].SubItems.Add(result.StatusString);
listView1.Items[result.SysCount - 1].SubItems.Add(result.AvailableUpdatesCount.ToString());
}
catch (Exception ex)
{}
//other stuff
}
}
The real problem is that the ListView updating code uses the wrong index to update items. It assumes the Status.SysCount property contains the correct index. This may be true if execution happens in sequence, but fails if execution runs in parallel - different threads can finish at different speeds and report progress out-of-order.
The actual problem can be fixed simply by using the ListViewItem object returned by ListViewItemCollection.Add
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (!backgroundWorker1.CancellationPending)
{
Status result = (Status)e.UserState;
Complete_label.Visible = true;
var newItem=listView1.Items.Add("");
newItem.SubItems.Add(result.SystemName);
newItem.SubItems.Add(result.StatusString);
newItem.SubItems.Add(result.AvailableUpdatesCount.ToString());
//other stuff
}
}
The code has more serious problems though - the State class tries to process data in parallel, storing the data in its own properties, then sending itself for reporting. Obviously, the data that gets displayed will always be changing.
A better option is either to create a new State instance inside the loop or, better yet, create a class only for reporting:
class StatusProgress
{
public string SystemName{get;set;}
public string StatusString{get;set;}
public int AvailableUpdatesCount {get;set;}
}
....
int sysCount=0;
Parallel.ForEach(namelist, line =>
{
var progress=new StatusProgress();
progress.SystemName = line;//file.ReadLine();
Status.sVariables result = new Status.sVariables();
result = OneSystem(line);
switch (result.BGWResult)
{
case -1:
progress.StatusString = "Login to server failed!";
break;
//other status are assigned here;
}
var count=Interlocked.Increment(ref sysCount);
}
worker.ReportProgress(count, progress);
});
Notice that instead of SysCount++ is use Interlocked.Increment to increase the value atomically and get a copy of the incremented value. If I didn't do that, multiple threads could modify SysCount before I had a chance to report progress.
The progress reporting code would change to use StateProgress
StatusProgress result = (StatusProgress)e.UserState;
Finally, the BackgroundWorker is obsolete as the Task Parallel Library offers everything the BGW did and more, in a far more lightweight manner. For example, you can cancel the parallel loop by using a CancellationToken and report progress in a type-safe manner using the Progress class.
Most asynchronous methods in .NET recognize CancellationToken and Progress which means you can report progress and cancel asynchronous tasks easily as shown here.
The code could be rewritten like this:
On a UI form:
private void ReportServerProgress(StatusProgress result)
{
Complete_label.Visible = true;
var newItem=listView1.Items.Add("");
newItem.SubItems.Add(result.SystemName);
newItem.SubItems.Add(result.StatusString);
newItem.SubItems.Add(result.AvailableUpdatesCount.ToString());
//other stuff
}
CancellationTokenSource _cts;
Progress<StatusProgress> _progress;
public void StartProcessiong()
{
_cts=new CancellationTokenSource();
_progress=new Progress<StatusProgress(progress=>ReportServerProgress(progress);
StartProcessing(/*input*/,_cts.Token,_progress);
}
public void CancelLoop()
{
if (_cts!=null)
_cts.Cancel();
}
The processing code can be on the same form or any other class. In fact, it's better to separate the UI from the processing code, especially when you have non-trivial processing, eg calling each server to determine its status
public void StartProcessing(/*input parameters*/,
CancellationTokenSource token,
IProgress<StatusProgress> progress)
{
.....
var po=new ParallelOptions();
po.CancellationToken=token;
Parallel.ForEach(namelist, po,line =>
{
var status=new StatusProgress();
status.SystemName = line;//file.ReadLine();
Status.sVariables result = new Status.sVariables();
result = OneSystem(line);
switch (result.BGWResult)
{
case -1:
progress.StatusString = "Login to server failed!";
break;
//other status are assigned here;
}
progress.Report(status);
}
}
Many asynchronous .NET methods accept a cancellation token, so you can pass it eg to a Web Service call and ensure both the loop and any outstanding long calls are cancelled.
Your results are all mixed up because you are using a parallel operation to write to global state, eg SystemName and StatusString, thus the contents of those global variables will end up all mixed up when you try to read and print their values.
You could introduce a lock, but this would completely defeat the point of the Parallel.ForEach. So either abandon he use of the Parallel.ForEach (which seems to serve no useful purpose in this instance) or you need to gather data and ensure it's sent to the reporter in a thread-safe fashion.
To further explain, let's examine the code:
this.SystemName = line; // <- the worker has now written to this, which is global to all workers
...
result = OneSystem(this.SystemName); // <- another worker may have overwritten SystemName at this point
...
this.StatusString = "Login to server failed!"; // <- again writing to shared variable
...
bag.Add(this); // <- now trying to "thread protect" already corrupted data
So if you must run the loop in parallel, each worker must update only its own isolated data then push that off to the GUI marshalling report method.
Related
I'm communication with an external device (PLC) and he requests data.
I have an event that checks when a value changes in my PLC (for example "NeedNewPosition" or "NeedNewBarValues")
I would like to change my code that it will handle them one by one. Sometimes it seems he's handling 2 of them at the same time. (probably since one takes longer than the other to finish)
I've read something about async methods and wait/tasks etc, but that seems a lot of work for something this simple.
The code:
private void PLCValueChanged(object sender, EventArgs e)
{
bool xDisplayValue = false;
PLCVar plcvariable = (PLCVar)sender;
string VarName = plcvariable.DisplayName;
switch (VarName)
{
case "NEEDNEWPOSITION": //Writing required position to PLC
if (Convert.ToBoolean(plcvariable.Value))
{
SearchNewPosition();
OPCclient.SendVarToPLC(OPCclient.SendPlcAllBarsFinished, "FALSE");
OPCclient.SendVarToPLC(OPCclient.SendPLCAllMagnetsFinished, "FALSE");
MagnetsInArea = GetMagnetsInWorkArea();
SymbolsInArea = GetSymbolsInWorkArea();
BarsInArea = GetBarsInWorkArea();
}
else
{
OPCclient.SendVarToPLC(OPCclient.SendPLCNewPositionIsSend, "FALSE");
}
break;
case "NEEDNEWBARVALUES": //Writing Bar Values to PLC
if (Convert.ToBoolean(plcvariable.Value))
{
OPCclient.SendVarToPLC(OPCclient.SendPLCBarStrippedOK, "FALSE");
OPCclient.SendVarToPLC(OPCclient.SendPLCBarMagnetsOK, "FALSE");
OPCclient.SendVarToPLC(OPCclient.SendPLCAllBarMagnetsLoose, "FALSE");
SetFirstBarValues();
OffsetsCalculated = false;
StartVisualisation?.Invoke(this, null); //%M59
}
else //if (!Convert.ToBoolean(plcvariable.Value))
{
OPCclient.SendVarToPLC(OPCclient.SendPLCBarDataIsSend, "FALSE");
}
break;
It sounds like you are looking for a Semaphore. Like the like/wiki says:
a semaphore is a variable or abstract data type used to control access to a common resource by multiple threads and avoid critical section problems in a concurrent system such as a multitasking operating system.
I.e. you can use the semaphore to "block" until a resource becomes available again.
You have multiple types of semaphores in C#, but the simplest to use is the SemaphoreSlim.
You can just define a static one for your singleton class instance
private static readonly SemaphoreSlim _semaphore = new(1, 1);
The 1,1 means: "1 is available, and there can only be 1".
Then in your code:
// take a semaphore, or wait until it is available
await _semaphore.WaitAsync(Timeout.Infinite, cancellationToken);
try
{
[.. your work...]
}
finally
{
// give the semaphore back
_semaphore.Release();
}
Note, I'm using await here, because this means the thread becomes available for other tasks. It will also wait indefinitely until a semaphore is available. The way to stop this is the cancallationToken.
You could wait for the processing of the first event to be finished using an AutoResetEvent:
using System.Threading;
// ...
// declare lock as a static class member
private static AutoResetEvent barsInAreaLoaded = new AutoResetEvent(false);
private void PLCValueChanged(object sender, EventArgs e)
{
// ...
switch (VarName)
{
case "NEEDNEWPOSITION":
if (Convert.ToBoolean(plcvariable.Value))
{
// ...
BarsInArea = GetBarsInWorkArea();
// signal waiting thread that bars are ready
barsInAreaLoaded.Set();
}
// ...
break;
case "NEEDNEWBARVALUES":
if (Convert.ToBoolean(plcvariable.Value))
{
// ...
// block until bars are ready
barsInAreaLoaded.WaitOne();
SetFirstBarValues();
// ...
}
// ...
break;
Note that this will only work if you are sure that the processing of two corresponding NEEDNEWPOSITION and NEEDNEWBARVALUES messages overlap. If some of those messages actually pile up this won't solve your problem and you should consider implementing some kind of message queue/pipeline.
I need to test if there's any memory leak in our application and monitor to see if memory usage increases too much while processing the requests.
I'm trying to develop some code to make multiple simultaneous calls to our api/webservice method. This api method is not asynchronous and takes some time to complete its operation.
I've made a lot of research about Tasks, Threads and Parallelism, but so far I had no luck. The problem is, even after trying all the below solutions, the result is always the same, it appears to be processing only two requests at the time.
Tried:
-> Creating tasks inside a simple for loop and starting them with and without setting them with TaskCreationOptions.LongRunning
-> Creating threads inside a simple for loop and starting them with and without high priority
-> Creating a list of actions on a simple for loop and starting them using
Parallel.Foreach(list, options, item => item.Invoke)
-> Running directly inside a Parallel.For loop (below)
-> Running TPL methods with and without Options and TaskScheduler
-> Tried with different values for MaxParallelism and maximum threads
-> Checked this post too, but it didn't help either. (Could I be missing something?)
-> Checked some other posts here in Stackoverflow, but with F# solutions that I don't know how to properly translate them to C#. (I never used F#...)
(Task Scheduler class taken from msdn)
Here's the basic structure that I have:
public class Test
{
Data _data;
String _url;
public Test(Data data, string url)
{
_data = data;
_url = url;
}
public ReturnData Execute()
{
ReturnData returnData;
using(var ws = new WebService())
{
ws.Url = _url;
ws.Timeout = 600000;
var wsReturn = ws.LongRunningMethod(data);
// Basically convert wsReturn to my method return, with some logic if/else etc
}
return returnData;
}
}
sealed class ThreadTaskScheduler : TaskScheduler, IDisposable
{
// The runtime decides how many tasks to create for the given set of iterations, loop options, and scheduler's max concurrency level.
// Tasks will be queued in this collection
private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
// Maintain an array of threads. (Feel free to bump up _n.)
private readonly int _n = 100;
private Thread[] _threads;
public TwoThreadTaskScheduler()
{
_threads = new Thread[_n];
// Create unstarted threads based on the same inline delegate
for (int i = 0; i < _n; i++)
{
_threads[i] = new Thread(() =>
{
// The following loop blocks until items become available in the blocking collection.
// Then one thread is unblocked to consume that item.
foreach (var task in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(task);
}
});
// Start each thread
_threads[i].IsBackground = true;
_threads[i].Start();
}
}
// This method is invoked by the runtime to schedule a task
protected override void QueueTask(Task task)
{
_tasks.Add(task);
}
// The runtime will probe if a task can be executed in the current thread.
// By returning false, we direct all tasks to be queued up.
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false;
}
public override int MaximumConcurrencyLevel { get { return _n; } }
protected override IEnumerable<Task> GetScheduledTasks()
{
return _tasks.ToArray();
}
// Dispose is not thread-safe with other members.
// It may only be used when no more tasks will be queued
// to the scheduler. This implementation will block
// until all previously queued tasks have completed.
public void Dispose()
{
if (_threads != null)
{
_tasks.CompleteAdding();
for (int i = 0; i < _n; i++)
{
_threads[i].Join();
_threads[i] = null;
}
_threads = null;
_tasks.Dispose();
_tasks = null;
}
}
}
And the test code itself:
private void button2_Click(object sender, EventArgs e)
{
var maximum = 100;
var options = new ParallelOptions
{
MaxDegreeOfParallelism = 100,
TaskScheduler = new ThreadTaskScheduler()
};
// To prevent UI blocking
Task.Factory.StartNew(() =>
{
Parallel.For(0, maximum, options, i =>
{
var data = new Data();
// Fill data
var test = new Test(data, _url); //_url is pre-defined
var ret = test.Execute();
// Check return and display on screen
var now = DateTime.Now.ToString("HH:mm:ss");
var newText = $"{Environment.NewLine}[{now}] - {ret.ReturnId}) {ret.ReturnDescription}";
AppendTextBox(newText, ref resultTextBox);
}
}
public void AppendTextBox(string value, ref TextBox textBox)
{
if (InvokeRequired)
{
this.Invoke(new ActionRef<string, TextBox>(AppendTextBox), value, textBox);
return;
}
textBox.Text += value;
}
And the result that I get is basically this:
[10:08:56] - (0) OK
[10:08:56] - (0) OK
[10:09:23] - (0) OK
[10:09:23] - (0) OK
[10:09:49] - (0) OK
[10:09:50] - (0) OK
[10:10:15] - (0) OK
[10:10:16] - (0) OK
etc
As far as I know there's no limitation on the server side. I'm relatively new to the Parallel/Multitasking world. Is there any other way to do this? Am I missing something?
(I simplified all the code for clearness and I believe that the provided code is enough to picture the mentioned scenarios. I also didn't post the application code, but it's a simple WinForms screen just to call and show results. If any code is somehow relevant, please let me know, I can edit and post it too.)
Thanks in advance!
EDIT1: I checked on the server logs that it's receiving the requests two by two, so it's indeed something related to sending them, not receiving.
Could it be a network problem/limitation related to how the framework manages the requests/connections? Or something with the network at all (unrelated to .net)?
EDIT2: Forgot to mention, it's a SOAP webservice.
EDIT3: One of the properties that I send (inside data) needs to change for each request.
EDIT4: I noticed that there's always an interval of ~25 secs between each pair of request, if it's relevant.
I would recommend not to reinvent the wheel and just use one of the existing solutions:
Most obvious choice: if your Visual Studio license allows you can use MS Load Testing Framework, most likely you won't even have to write a single line of code: How to: Create a Web Service Test
SoapUI is a free and open source web services testing tool, it has some limited load testing capabilities
If for some reasons SoapUI is not suitable (i.e. you need to run load tests in clustered mode from several hosts or you need more enhanced reporting) you can use Apache JMeter - free and open source multiprotocol load testing tool which supports web services load testing as well.
A good solution to create load tests without write a own project is use this service https://loader.io/targets
It is free for small tests, you can POST Parameters, Header,... and you have a nice reporting.
Isnt the "two requests at a time" the result of the default maxconnection=2 limit on connectionManagement?
<configuration>
<system.net>
<connectionManagement>
<add address = "http://www.contoso.com" maxconnection = "4" />
<add address = "*" maxconnection = "2" />
</connectionManagement>
</system.net>
</configuration>
My favorite load testing library is NBomber. It has an easy and powerful API, realistic user simulations, and provides you with nice HTML reports about latency and requests per second.
I used it to test my API and wrote an article about how I did it.
I have a fairly simple WPF application that uses Entity Framework. The main page of the application has a list of records that I am getting from a database on startup.
Each record has a picture, so the operation can be a little slow when the wireless signal is poor. I'd like this (and many of my SQL operations) to perform in the background if possible. I have async/await setup and at first it seemed to be working exactly as I wanted but now I'm seeing that my application is becoming unresponsive when accessing the DB.
Eventually I'm thinking I'm going to load up the text in one query and the images in another background operation and load them as they come in. This way I get the important stuff right away and the pictures can come in in the background, but the way things are going it's still looking like it will lock up if I do this.
On top of that, I'm trying to implement something to handle connectivity issues (in case the wifi cuts out momentarily) so that the application notifies the user of a connection issue, automatically retries a few times, etc. I put a try catch for SQL exception which seems to be working for me, but the whole application locks up for about a minute while it is trying to connect to the DB.
I tried testing my async/await using await Task.Delay() and everything is very responsive as expected while awaiting the delay, but everything locks up when awaiting the .ToListAsync(). Is this normal and expected? My understanding of async/await is pretty limited.
My code is kind of messy (I'm new) but it does what I need it to do for the most part. I understand there's probably plenty of improvements I can make and better ways to do things, but one step at a time here. My main goal right now is to keep the application from crashing during database accessing exceptions and to keep the user notified of what the application is doing (searching, trying to access db, unable to reach DB and retrying, etc) as opposed to being frozen, which is what they're going to think when they see it being unresponsive for over a minute.
Some of my code:
In my main view model
DataHelper data = new DataHelper();
private async void GetQualityRegisterQueueAsync()
{
try
{
var task = data.GetQualityRegisterAsync();
IsSearching = true;
await task;
IsSearching = false;
QualityRegisterItems = new ObservableCollection<QualityRegisterQueue>(task.Result);
OrderQualityRegisterItems();
}
catch (M1Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine("QualityRegisterLogViewModel.GetQualityRegisterQueue() Operation Failed");
}
}
My Data Helper Class
public class DataHelper
{
private bool debugging = false;
private const int MAX_RETRY = 2;
private const double LONG_WAIT_SECONDS = 5;
private const double SHORT_WAIT_SECONDS = 0.5;
private static readonly TimeSpan longWait = TimeSpan.FromSeconds(LONG_WAIT_SECONDS);
private static readonly TimeSpan shortWait = TimeSpan.FromSeconds(SHORT_WAIT_SECONDS);
private enum RetryableSqlErrors
{
ServerNotFound = -1,
Timeout = -2,
NoLock = 1204,
Deadlock = 1205,
}
public async Task<List<QualityRegisterQueue>> GetQualityRegisterAsync()
{
if(debugging) await Task.Delay(5000);
var retryCount = 0;
using (M1Context m1 = new M1Context())
{
for (; ; )
{
try
{
return await (from a in m1.QualityRegisters
where (a.qanClosed == 0)
//orderby a.qanAssignedDate descending, a.qanOpenedDate
orderby a.qanAssignedDate.HasValue descending, a.qanAssignedDate, a.qanOpenedDate
select new QualityRegisterQueue
{
QualityRegisterID = a.qanQualityRegisterID,
JobID = a.qanJobID.Trim(),
JobAssemblyID = a.qanJobAssemblyID,
JobOperationID = a.qanJobOperationID,
PartID = a.qanPartID.Trim(),
PartRevisionID = a.qanPartRevisionID.Trim(),
PartShortDescription = a.qanPartShortDescription.Trim(),
OpenedByEmployeeID = a.qanOpenedByEmployeeID.Trim(),
OpenedByEmployeeName = a.OpenedEmployee.lmeEmployeeName.Trim(),
OpenedDate = a.qanOpenedDate,
PartImage = a.JobAssembly.ujmaPartImage,
AssignedDate = a.qanAssignedDate,
AssignedToEmployeeID = a.qanAssignedToEmployeeID.Trim(),
AssignedToEmployeeName = a.AssignedEmployee.lmeEmployeeName.Trim()
}).ToListAsync();
}
catch (SqlException ex)
{
Debug.WriteLine("SQL Exception number = " + ex.Number);
if (!Enum.IsDefined(typeof(RetryableSqlErrors), ex.Number))
throw new M1Exception(ex.Message, ex);
retryCount++;
if (retryCount > MAX_RETRY) throw new M1Exception(ex.Message, ex); ;
Debug.WriteLine("Retrying. Count = " + retryCount);
Thread.Sleep(ex.Number == (int)RetryableSqlErrors.Timeout ?
longWait : shortWait);
}
}
}
}
}
Edit: Mostly looking for general guidance here, though a specific example of what to do would be great. For these types of operations where I am downloading data, is it just a given that if I need the application to be responsive I need to be making multiple threads? Is that a common solution to this type of problem? Is this not something I should be expecting async/await to solve?
If you call this method from your UI thread, you will overload the capture of UI thread context and back on itself. Also, your service will not be necessarily "Performant" because it must wait until the UI thread is free before it can continue.
The solution is simple: just call the method passing the ConfigureAwait "false" parameter when you made the call.
.ToListAsync().ConfigureAwaiter(false);
I hope it helps
I have a service layer project on an MVC 5 ASP.NET application I am creating on .NET 4.5.2 which calls out to an External 3rd Party WCF Service to Get Information asynchronously. An original method to call external service was as below (there are 3 of these all similar in total which I call in order from my GetInfoFromExternalService method (note it isnt actually called that - just naming it for illustration)
private async Task<string> GetTokenIdForCarsAsync(Car[] cars)
{
try
{
if (_externalpServiceClient == null)
{
_externalpServiceClient = new ExternalServiceClient("WSHttpBinding_IExternalService");
}
string tokenId= await _externalpServiceClient .GetInfoForCarsAsync(cars).ConfigureAwait(false);
return tokenId;
}
catch (Exception ex)
{
//TODO plug in log 4 net
throw new Exception("Failed" + ex.Message);
}
finally
{
CloseExternalServiceClient(_externalpServiceClient);
_externalpServiceClient= null;
}
}
So that meant that when each async call had completed the finally block ran - the WCF client was closed and set to null and then newed up when another request was made. This was working fine until a change needed to be made whereby if the number of cars passed in by User exceeds 1000 I create a Split Function and then call my GetInfoFromExternalService method in a WhenAll with each 1000 - as below:
if (cars.Count > 1000)
{
const int packageSize = 1000;
var packages = SplitCarss(cars, packageSize);
//kick off the number of split packages we got above in Parallel and await until they all complete
await Task.WhenAll(packages.Select(GetInfoFromExternalService));
}
However this now falls over as if I have 3000 cars the method call to GetTokenId news up the WCF service but the finally blocks closes it so the second batch of 1000 that is attempting to be run throws an exception. If I remove the finally block the code works ok - but it is obviously not good practice to not be closing this WCF client.
I had tried putting it after my if else block where the cars.count is evaluated - but if a User uploads for e.g 2000 cars and that completes and runs in say 1 min - in the meantime as the user had control in the Webpage they could upload another 2000 or another User could upload and again it falls over with an Exception.
Is there a good way anyone can see to correctly close the External Service Client?
Based on the related question of yours, your "split" logic doesn't seem to give you what you're trying to achieve. WhenAll still executes requests in parallel, so you may end up running more than 1000 requests at any given moment of time. Use SemaphoreSlim to throttle the number of simultaneously active requests and limit that number to 1000. This way, you don't need to do any splits.
Another issue might be in how you handle the creation/disposal of ExternalServiceClient client. I suspect there might a race condition there.
Lastly, when you re-throw from the catch block, you should at least include a reference to the original exception.
Here's how to address these issues (untested, but should give you the idea):
const int MAX_PARALLEL = 1000;
SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(MAX_PARALLEL);
volatile int _activeClients = 0;
readonly object _lock = new Object();
ExternalServiceClient _externalpServiceClient = null;
ExternalServiceClient GetClient()
{
lock (_lock)
{
if (_activeClients == 0)
_externalpServiceClient = new ExternalServiceClient("WSHttpBinding_IExternalService");
_activeClients++;
return _externalpServiceClient;
}
}
void ReleaseClient()
{
lock (_lock)
{
_activeClients--;
if (_activeClients == 0)
{
_externalpServiceClient.Close();
_externalpServiceClient = null;
}
}
}
private async Task<string> GetTokenIdForCarsAsync(Car[] cars)
{
var client = GetClient();
try
{
await _semaphoreSlim.WaitAsync().ConfigureAwait(false);
try
{
string tokenId = await client.GetInfoForCarsAsync(cars).ConfigureAwait(false);
return tokenId;
}
catch (Exception ex)
{
//TODO plug in log 4 net
throw new Exception("Failed" + ex.Message, ex);
}
finally
{
_semaphoreSlim.Release();
}
}
finally
{
ReleaseClient();
}
}
Updated based on the comment:
the External WebService company can accept me passing up to 5000 car
objects in one call - though they recommend splitting into batches of
1000 and run up to 5 in parallel at one time - so when I mention 7000
- I dont mean GetTokenIdForCarAsync would be called 7000 times - with my code currently it should be called 7 times - i.e giving me back 7
token ids - I am wondering can I use your semaphore slim to run first
5 in parallel and then 2
The changes are minimal (but untested). First:
const int MAX_PARALLEL = 5;
Then, using Marc Gravell's ChunkExtension.Chunkify, we introduce GetAllTokenIdForCarsAsync, which in turn will be calling GetTokenIdForCarsAsync from above:
private async Task<string[]> GetAllTokenIdForCarsAsync(Car[] cars)
{
var results = new List<string>();
var chunks = cars.Chunkify(1000);
var tasks = chunks.Select(chunk => GetTokenIdForCarsAsync(chunk)).ToArray();
await Task.WhenAll(tasks);
return tasks.Select(task => task.Result).ToArray();
}
Now you can pass all 7000 cars into GetAllTokenIdForCarsAsync. This is a skeleton, it can be improved with some retry logic if any of the batch requests has failed (I'm leaving that up to you).
I've a got a problem with the infamous message "The thread xxx has exited with code 0 (0x0)".
In my code I have a main class called "Load" that starts with a Windows Form load event:
public class Load
{
public Load()
{
Device[] devices = GetDevices(); // Get an array of devices from an external source
for (int i = 0; i < devices.Length; i++)
{
DeviceDiagnosticCtrl deviceDiagnostic = new DeviceDiagnosticCtrl(devices[i].name);
}
}
}
Inside the constructor, for each generic device read from an external source, I initialize a custom diagnostic class that runs a thread:
public class DeviceDiagnosticCtrl
{
private Thread diagnosticController;
private volatile bool diagnosticControllerIsRunning = false;
public DeviceDiagnosticCtrl(string _name)
{
// Thread initialization
this.diagnosticController = new Thread(new ThreadStart(this.CheckDiagnostic));
this.diagnosticController.Start();
this.diagnosticControllerIsRunning = true;
}
private void CheckDiagnostic()
{
while (this.diagnosticControllerIsRunning)
{
try
{
// Custom 'Poll' message class used to request diagnostic to specific device
Poll poll = new Poll();
// Generic Message result to diagnostic request
IGenericMessage genericResult;
// Use a custom driver to send diagnostic request
SendSyncMsgResult res = this.customDriver.SendSyncMessage(poll, out genericResult);
switch (res)
{
case SendSyncMessageResult.GOOD:
{
// Log result
}
break;
case SendSyncMessageResult.EXCEPTION:
{
// Log result
}
break;
}
Thread.Sleep(this.customDriver.PollScantime);
}
catch (Exception ex)
{
// Loggo exception
}
}
}
}
When I run the above code in debug mode I always read 8 devices from external source, and for each of them I continuously run a managed thread to retrieve diagnostic.
My problem is that randomly one or more of the 8 threads I expect from the code above exit with code 0, without any exception.
I've started/restarted the code in debug mode a lot of time, and almost everytime one of the thread exits.
I've read somewhere (i.e. this SO question) that it could depends of Garbage Collector action, but I'm not too sure if this is my case - and how to prevent it.
Do someone see something strange/wrong in the sample code I posted above? Any suggestion?
'while (this.diagnosticControllerIsRunning)' is quite likely to fail immediate, in which case the thread drops out. It's no good starting the thread and THEN setting 'this.diagnosticControllerIsRunning = true;' - you're quite likely to be too late.
Bolt/stable-door. Something like:
do{
lengthyStuff with Sleep() in it
}
while (this.diagnosticControllerRun);
Copied from Here
Right click in the Output window when you're running your program and
uncheck all of the messages you don't want to see (like Thread Exit
messages).