I've created a class with a method which is called by different threads (outside the class). Before calling the method, the threads update their own set of parameters by updating a dictionary of properties in the class. I parameterise the method by then selecting the appropriate item from the dictionary.
The method has various variables in it and performs various calculations. If the calculations return a certain value, the method passes control to one of several external APIs, waits for the API to pass back a value, writes the value to a database and then exits.
I want each call of the method to be entirely independent - ie thread A should not interfere with the value of variables in thread B and visa versa. My initial reading suggests this is not the model used for threading in C#, but that each variable within the method is accessed and updated by any thread - ie a Datarace (? may be using incorrect term here). Locking is not ideal for me, as time to execution for each thread is important. (If I lock, I understand that the threads would then be sequentially executed, since they reference the same method/variables).
I think I achieved the behaviour I was looking for using a threadpool in python, although it could just be that I got lucky during my limited usage!
Is it possible to have the method calls / threads entirely independent of each other in C#, as described above? If so, please could you point me in the direction of what I should be looking for?
Many thanks.
Update: Here's an excerpt from the code in what I'm trying to do, as requested. I've taken out most of the code which relates to calculations and just left comments and how the different classes call threads, etc.
//Websockets sharp on-message event
//Within namespace1.class1
ws.OnMessage += (sender, e) =>
{ //write a whole lot of pricing data to a dictionary ("price dictionary") and identify whether we should re-evealuate our pricing conditions
if (blnReevaluatePricingConditions)
{
nsStrategy.csStrategy objPriceCheck = new nsStrategy.csStrategy();
objPriceCheck.EvaluatePricingConditions(strExchange, strInstrument);
}
}
// within nsStrategy.csStrategy
public void EvaluatePricingConditions(string strExchange = "", string strInstrument = "")
{ //evaluate the pricing conditions (stored in a dictionary ("conditions dictionary")) against the price dictionary
//If conditions evaluate to trade = true, create a list of >1 sets of trade parameters to execute using REST API
nsREST.csREST myRESTObject = new nsREST.csREST();
nsREST.MarketOrder[] MOs = new nsREST.MarketOrder[n]; //n>1 - MOs stands for Market Orders
//Populate list
//...
//Execute by passing the list to a method in the csREST class object just created
myRESTObject.MarketOrder(MOs);
//Wait for completion of REST API calls
//Update db with parameters from MOs list
//End method
}
//within nsRest.csRest
public void MarketOrder(MarketOrder[] mktOs)
{
//Create a list of exchange methods (i.e. parameters for the exchange APIs)
ExchangeMethod[] lstExchangeMethods = new ExchangeMethod[mktOs.GetUpperBound(0)+1];
//Create a list of objects populted from the JSON the exchanges return
RestReturns[] rstReturn = new RestReturns[mktOs.GetUpperBound(0)+1];
//Populate the lstExchangeMEthods...and call a method passing it the list
rstReturn = threadedExchangeMethod(lstExchangeMethods);
}
public RestReturns[] threadedExchangeMethod(ExchangeMethod[] lstExchangeMethods)
{ //Executes the ExchangeMethods passed to it simultaneously, then waits for response from each.
List<Thread> lstThreads = new List<Thread>();
RESTGlobalVars.ExchangeMethodList = lstExchangeMethods;
//Now add a new thread to the list and start it with the parameters below
for (int i = 0; i <= lstExchangeMethods.GetUpperBound(0); i++)
{
lstThreads.Add(new Thread(ThreadedExchangeMethodExecution));
Thread.Sleep(5); //allow a tiny bit of time for the first thread to update to completed status
lstThreads[lstThreads.Count() - 1].Start();
}
}
private void ThreadedExchangeMethodExecution()
{
//Loop through the class-level list of exchangemethod parameters, set blnCompleted to true so it's not picked again by the next thread and call the ExchangeMethods method with the parameters
for (int i = 0; i <= RESTGlobalVars.ExchangeMethodList.GetUpperBound(0); i++)
{
if (RESTGlobalVars.ExchangeMethodList[i].Completed == false)
{
RESTGlobalVars.ExchangeMethodList[i].Completed = true;
RestReturns rstResponse = ExchangeMethods(RESTGlobalVars.ExchangeMethodList[i].Exchange, RESTGlobalVars.ExchangeMethodList[i].Method, RESTGlobalVars.ExchangeMethodList[i].Parameters);
rstResponse.Exchange = RESTGlobalVars.ExchangeMethodList[i].Exchange;
RESTGlobalVars.ExchangeMethodList[i].Response = rstResponse;
}
}
}
public class ExchangeMethod
{
public string Exchange { get; set; }
public string Method { get; set; }
public string Parameters { get; set; }
public Boolean Completed { get; set; }
public RestReturns Response { get; set; }
}
public class MarketOrder
{
public string StrategyName { get; set; }
public string Exchange { get; set; }
public string Instrument { get; set; }
public string BuyOrSell { get; set; }
public double VolumeInUSD { get; set; }
}
public static class RESTGlobalVars
{
public static ExchangeMethod[] ExchangeMethodList { get; set; }
public static Dictionary<string, RateLimits> dtRateWait = new Dictionary<string, RateLimits>();
}
In terms of the workflow, I receive a websocketssharp message (which is a background thread, I believe). This could be from one of several websockets feeds, each of which is contained in its own namespace and class, and each of which continues with the same logic. I evaluate the message, place prices from it in a dictionary and then call a method to evaluate the prices to identify whether we should trade. This method then does some calcs and (if applicable) creates a list of trade parameters, then calls a method called MarketOrder, passing the list of parameters to it. The Market Order parameters are turned into an analogous (slightly redundant) list of exchangemethod parameters, each with a 'completed' parameter set to false, which are written to a class-level ('global') list. We then create a new thread for each item in the list. Each thread checks the global list, and pulls the first uncompleted item from the list. It sets the completed parameter to true, and then executes the API call.
Thanks to those who have responded so far - I'm reading up on your suggestions...