I was trying to create an integration test for my service where 100 clients would connect, login, send request, and log all responses for some configurable amount of time.
I was build a class for the client using async sockets and it works fine. I started them all up using Task and Task.Factory, sent the login, and posted receives every time I got data, until the time expired and then I called shutdown on them.
It seems these never really run in parallel. Sometimes I might get 35ish running at once, sometimes a little more. I assume the task scheduler is running them when it seems fit rather than all at once.
Now I understand that I cannot truly have 100 threads running simultaneous, but I want to guarantee that all 100 are started and that the OS is context switching back and forth attempting to execute them all.
In the end, I want to simulate a large number of clients connected to my service all getting a stream of data.
What construct do I use if Task does not work?
Current Attempt:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IntegrationTests
{
class Program
{
static void Main(string[] args)
{
string server = ConfigurationManager.AppSettings["server"];
int port = int.Parse(ConfigurationManager.AppSettings["port"]);
int numClients = int.Parse(ConfigurationManager.AppSettings["numberOfClients"]);
TimeSpan clientLifetime = TimeSpan.Parse(ConfigurationManager.AppSettings["clientLifetime"]);
TimeSpan timeout = TimeSpan.Parse(ConfigurationManager.AppSettings["timeout"]);
TimeSpan reconnectInterval = TimeSpan.Parse(ConfigurationManager.AppSettings["reconnectInterval"]);
List<string> clientIds = ConfigurationManager.GetSection("clientIds") as List<string>;
try
{
// SNIP configure logging
// Create the specified number of clients, to carry out test operations, each on their own threads
Task[] tasks = new Task[numClients];
for(int count = 0; count < numClients; ++count)
{
var index = count;
tasks[count] = Task.Factory.StartNew(() =>
{
try
{
// Reuse client Ids, if there are more clients then clientIds.
// Keep in mind that tasks are not necessarily started in the order they were created in this loop.
// We may see client id 1 be assigned client id 2 if another client was started before it, but we
// are using all clientIds
string clientId = null;
if (numClients < clientIds.Count)
{
clientId = clientIds[index];
}
else
{
clientId = clientIds[index % clientIds.Count];
}
// Create the actual client
Client client = new Client(server, port, clientId, timeout, reconnectInterval);
client.Startup();
// Will make an sync request issue a recv.
// Everytime we get a reponse, it will be logged and another recv will be posted.
// This will continue until shutdown is called
client.MakeRequest(symbol);
System.Threading.Thread.Sleep(clientLifetime);
client.Shutdown();
}
catch(Exception e)
{
// SNIP - Log it
}
});
}
Task.WaitAll(tasks);
}
catch (Exception e)
{
// SNIP - Log it
}
}
}
}