Я редко касаюсь многопоточности своим кодом, но столкнулся со значительным давлением, чтобы сократить время выполнения, и поэтому пытался использовать параллельный цикл. ForEach.
Код выполняет расчеты по заявкам (Предметам). Элементы были загружены в List<List<Item>>
(внешний список представляет собой элементы с одинаковым идентификатором, а внутренний список представляет собой различные элементы). В конце я включил класс Item.
List<List<Item>> queryItemsByAcceptID = phyBidList.GroupBy(bids => bids.acceptID)
.Select(group => group.ToList())
.ToList();
Все заявки имеют свойство (расчетный период), которое разделяет их по времени, например период 1 = 00: 00-00: 30, период 2 = 00: 30-01: 00 и т. д.
Ставка периода 2 не нуждается в информации от ставки периода 1.
Поэтому я разделил заявки на периоды их расчета, чтобы выполнить вычисления в параллели. ForEach.
List<Item> phyBidList = new List<Item>();
var queryMassBySetPeriod = phyBidList.GroupBy(x => x.settlementPeriod)
.Select(group => group.ToList())
.ToList();
Однако, когда я запускаю код, я получаю противоречивые результаты как с «непараллельным кодом», так и с предыдущими выходными данными при многократном запуске. Это заставляет меня думать, что мой код не является «потокобезопасным», так как «непараллельный код» является правильным и непротиворечивым (но слишком медленным).
Безопасен ли этот поток? И что я должен сделать, чтобы получить последовательные результаты?
Мне интересно, должен ли я закрывать дневной свет из всего ...
acceptIdItem.FPN.Add(fpn);
acceptIdItem.qAboPosArea.Add(tempQABOposArea);
acceptIdItem.qAboNegArea.Add(tempQABOnegArea);
Однако я не уверен, подходит ли блокировка, потому что потоки не (или, по крайней мере, не должны) получать доступ к одним и тем же переменным ... и потому, что информация из других блоков не требуется, и ставки только идут через расчет один раз.
P.S. Я включил код ниже, я пытался удалить то, что не считаю необходимым, чтобы сделать его короче и легче для чтения.
Parallel.ForEach(queryMassBySetPeriod, block =>
{
Console.WriteLine("GroupBy UnitID");
var queryItemsByUnitID = block.GroupBy(bids => bids.unitID)
.Select(group => group.ToList())
.ToList();
Console.WriteLine("GroupBy AcceptID");
queryItemsByAcceptID = block.GroupBy(bids => bids.acceptID)
.Select(group => group.ToList())
.ToList();
Console.WriteLine("Beginning mass interpretation...");
foreach (var list in queryItemsByAcceptID)
{
int bY = 0;
foreach (var acceptIdItem in list)
{
DateTime fromTime = acceptIdItem.fromTime;
DateTime toTime = acceptIdItem.toTime;
TimeSpan duration = toTime - fromTime;
for (int i = 0; i < (duration.Minutes); i++) //qTime fix (duration.Minutes + 1)
{
var queryPNdata = (from item in PNList
where item.unitID == acceptIdItem.unitID && item.fromTime <= fromTime && item.toTime >= fromTime
select item).FirstOrDefault();
int time = (acceptIdItem.qTimes[i] - acceptIdItem.fromTime).Minutes;
float boa = MathHelper.calcBOA(acceptIdItem.fromLevel, acceptIdItem.toLevel, (duration).Minutes, time);
float fpn = MathHelper.calcFPN(queryPNdata.fromLevel, queryPNdata.toLevel, duration.Minutes, time);
acceptIdItem.qTimes.Add(fromTime + i * ((toTime - fromTime) / duration.Minutes));
acceptIdItem.boa.Add(boa);
acceptIdItem.FPN.Add(fpn);
string[] tempBOUR = new string[6]; string[] tempBOLR = new string[6];
float[] tempQABOneg = new float[6]; float[] tempQABOpos = new float[6];
for (int k = 1; k < 7; k++)
{
//calculate tempBOUR/ tempBOLR/ tempQABOpos/ tempQABOneg
}
acceptIdItem.BOUR.Add(tempBOUR);
acceptIdItem.BOLR.Add(tempBOLR);
acceptIdItem.qAboPos.Add(tempQABOpos);
acceptIdItem.qAboNeg.Add(tempQABOneg);
}
int aZ = 0; //declared outside the loop to access later
for (aZ = 0; aZ < (acceptIdItem.qAboNeg.Count() - 1); aZ++)
{
float[] tempQABOnegArea = new float[6]; float[] tempQABOposArea = new float[6];
for (int k = 1; k < 7; k++)
{
//calculate tempQABOnegArea/ tempQABOposArea
}
acceptIdItem.qAboPosArea.Add(tempQABOposArea);
acceptIdItem.qAboNegArea.Add(tempQABOnegArea);
}
bY++;
}
}
});
В начале назначается информация и класс добавляется в список (phyBidList). Вот этот класс ...
class Item
{
public String unitID, acceptID, prevAcceptID, type;
public DateTime fromTime, toTime, acceptTime;
public int settlementPeriod, duration;
public List<DateTime> qTimes = new List<DateTime>();
public List<string[]> BOLR = new List<string[]>();
public List<string[]> BOUR = new List<string[]>();
public List<float[]> qAboNeg = new List<float[]>();
public List<float[]> qAboPos = new List<float[]>();
public List<float> boa = new List<float>();
public List<float> prevBOA = new List<float>();
public List<float> FPN = new List<float>();
public List<float[]> qAboNegArea = new List<float[]>();
public List<float[]> qAboPosArea = new List<float[]>();
}
**** ***** Редактировать 1025 *
В ответ на @ calum-mcveigh я изменил списки на concurrentBags с использованием приведенного ниже фрагмента и других соответствующих изменений. Тем не менее, это все еще дает противоречивые результаты. Я поставил полный код здесь https://pastebin.com/EgnE2285
ConcurrentBag<ConcurrentBag<Item>> queryMass = new ConcurrentBag<ConcurrentBag<Item>>();
foreach (var items in queryMassBySetPeriod)
{
ConcurrentBag<Item> item = new ConcurrentBag<Item>();
foreach (var bid in items)
{
item.Add(bid);
}
queryMass.Add(item);
}