0

我创建了一个类,该类具有由不同线程(在类外)调用的方法。在调用该方法之前,线程通过更新类中的属性字典来更新它们自己的参数集。我通过从字典中选择适当的项目来参数化该方法。

该方法中包含各种变量并执行各种计算。如果计算返回某个值,则该方法将控制权传递给几个外部 API 之一,等待 API 传回一个值,将该值写入数据库,然后退出。

我希望该方法的每个调用都是完全独立的——即线程 A 不应干扰线程 B 中变量的值,反之亦然。我最初的阅读表明这不是用于 C# 中的线程化的模型,而是方法中的每个变量都可以由任何线程访问和更新 - 即 Datarace(?可能在这里使用了不正确的术语)。锁定对我来说并不理想,因为每个线程的执行时间很重要。(如果我锁定,我知道线程将被顺序执行,因为它们引用相同的方法/变量)。

我实现了我在 python 中使用线程池的行为,尽管这可能只是我在有限的使用期间很幸运!

如上所述,是否可以在 C# 中让方法调用/线程完全相互独立?如果是这样,请您指出我应该寻找的方向吗?

非常感谢。

更新:根据要求,这是我正在尝试做的代码的摘录。我已经删除了大部分与计算相关的代码,只留下了注释以及不同的类如何调用线程等。

    //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>();
    }

在工作流程方面,我收到一条 websocketssharp 消息(我相信这是一个后台线程)。这可能来自几个 websocket 提要之一,每个提要都包含在自己的命名空间和类中,并且每个提要都以相同的逻辑继续。我评估消息,将其中的价格放入字典中,然后调用一个方法来评估价格以确定我们是否应该交易。然后,此方法进行一些计算并(如果适用)创建交易参数列表,然后调用名为 MarketOrder 的方法,将参数列表传递给它。市价单参数被转换为一个类似的(稍微冗余的)交换方法参数列表,每个参数都有一个设置为 false 的“已完成”参数,这些参数被写入一个类级别(“全局”)列表。然后我们为列表中的每个项目创建一个新线程。每个线程检查全局列表,并从列表中拉出第一个未完成的项目。它将完成的参数设置为 true,然后执行 API 调用。

感谢迄今为止做出回应的人-我正在阅读您的建议...

4

1 回答 1

0

所有线程都有一定程度的依赖。它们都共享相同的进程和内存工作集。不同之处在于每个线程都可以有 CPU 时间,这允许所有线程同时工作。

即使它们共享相同的数据,使用不会被另一个线程引用的数据也是完全安全的。

ie :如果你有变量aand ,如果线程 2 只能访问b,线程 1 可以安全访问,反之亦然。ab

在您的情况下,即使一个线程使用的所有值都与另一个不同,字典也与您的所有线程共享访问权限。

有很多方法可以做到这一点。其中之一是创建一个类,该类将保存线程将使用的参数,并且可能还具有一些属性,以便线程可以存储它的结果(如果有)。

因此,在启动线程之前,弄清楚每个线程的数据子集是什么,并创建该数据类并在您想要启动它时将该类传递给线程。然后,一旦完成,您就可以在对象中获得结果,并且一旦所有线程都返回到主线程,就可以对它执行任何您需要的操作。

这是将参数传递给线程的一个非常简单的示例

于 2018-06-28T10:16:30.273 回答