0

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...

  • 1
    Do you need to share state between threads? If no, why don't make your class stateless? – Dennis Jun 28 '18 at 10:02
  • "by updating a dictionary of properties in the class" - in the class or in an object? – bommelding Jun 28 '18 at 10:07
  • 3
    Add some code samples, preferably an short [mcve]. Your terminology is not exact enough. – bommelding Jun 28 '18 at 10:07
  • 1
    Create an instance of the class per thread, that way they get their own variable instances. – Ross Miller Jun 28 '18 at 10:10
  • The term you are not sure is called a "race condition" meaning one thread might try to read a value while another want to write to it. – Franck Jun 28 '18 at 10:16
  • @RossMiller, I think maybe my problem is that the websocketssharp on-message event I am receiving is a background (or at least child/non-main) thread. Even if in the on-event code I then instantiate an object of the class containing the method I want to call, that object may be shared with the next on-message event. Is that reasonable? (and thanks!) – Douglas Lorimer Jun 28 '18 at 12:18
  • @bommelding, The dictionary is declared as a property within a public static class. I completely agree re not being exact enough, as I have insufficient expertise to even frame my question properly! I have tried to include some code, but it is neither minimal, complete nor verifiable, so apologies in advance, and thanks for the input. – Douglas Lorimer Jun 28 '18 at 12:20
  • When you receive your event, copy your event arguments into new variables. Perhaps include them as part of the class constructor. Basically you want all information your thread uses to be it's own instance for that thread specifically. I treat threads as new programs in their own right and require as much independent data as possible. – Ross Miller Jun 28 '18 at 12:27
  • Thanks @RossMiller. I'll take your comments back to the drawing board. Would upvote your comments, but don't have enough reputation! – Douglas Lorimer Jun 29 '18 at 07:31

1 Answers1

0

All thread are depended to a certain extent. They all share the same process and memory working set. The difference is that each thread can have CPU time which allow for all thread to work simultaneously.

Even if they share the same data it is totally safe to use data that wont be referenced by another thread.

i.e : If you have variable a and b, Thread 1 can access a safely if Thread 2 only access b and vice versa.

In your case the dictionary is sharing access with all your threads even if all the values used by a thread are different than another.

There are many ways to this. One of them is to create a class that will hold the parameters that the thread will use and probably also have some property so the thread can store it's results (if there is).

So just before starting the threads, figure out what are the data subset for each of them and create that data class and pass the class to the thread when you want to start it. Then once it finishes you have the results in the objects and can do whatever you need to it once all thread returned to the main thread.

Here's a very simple example of passing parameter to a thread

Franck
  • 4,438
  • 1
  • 28
  • 55