#c# #multithreading #xamarin.forms #entity-framework-core
#c# #многопоточность #xamarin.forms #сущность-фреймворк-ядро
Вопрос:
Я работал над хобби-проектом, разрабатываемым на C # Xamarin Forms Prism EF Core Sqlite, отладка в приложении UWP.
Я написал следующий код для хранения тиковых данных, полученных от брокера, в Sqlite.
Во-первых, обратный вызов OnTick, который получает тики (прибл. 1 тик в секунду на инструмент):
private void OnTick(Tick tickData)
{
foreach (var instrument in IntradayInstruments.Where(i => i.InstrumentToken == tickData.InstrumentToken))
{
instrument.UpdateIntradayCandle(tickData);
}
}
И метод UpdateIntradayCandle:
public void UpdateIntradayCandle(Tick tick)
{
if (LastIntradayCandle != null)
{
if (LastIntradayCandle.Open == 0m)
{
LastIntradayCandle.Open = tick.LastPrice;
}
if (LastIntradayCandle.High < tick.LastPrice)
{
LastIntradayCandle.High = tick.LastPrice;
}
if (LastIntradayCandle.Low == 0m)
{
LastIntradayCandle.Low = tick.LastPrice;
}
else if (LastIntradayCandle.Low > tick.LastPrice)
{
LastIntradayCandle.Low = tick.LastPrice;
}
LastIntradayCandle.Close = tick.LastPrice;
}
}
LastIntradayCandle является свойством:
object _sync = new object();
private volatile IntradayCandle _lastIntradayCandle;
public IntradayCandle LastIntradayCandle
{
get
{
lock (_sync)
{
return _lastIntradayCandle;
}
}
set
{
lock (_sync)
{
_lastIntradayCandle = value;
}
}
}
Теперь LastIntradayCandle периодически меняется, скажем, через 5 минут, и для обновления устанавливается новая свеча из другого потока, поступающего из системы.Многопоточность.Таймер, который должен запускаться каждые 5 м.
public void AddNewIntradayCandle()
{
if (LastIntradayCandle != null)
{
LastIntradayCandle.IsClosed = true;
}
var newIntradayCandle = new IntradayCandle { Open = 0m, High = 0m, Low = 0m, Close = 0m };
LastIntradayCandle = newIntradayCandle;
IntradayCandles.Add(newIntradayCandle);
}
Теперь проблема в том, что я получаю 0 в тех открытых, высоких или низких, но не в закрытых, открытых с наибольшим количеством нулей. Это происходит очень случайно.
Я думаю, что если какое-либо из значений Open, High, Low или Close обновляется, это означает, что галочка имеет значение для захвата, но каким-то образом одно или несколько назначений в методе UpdateIntradayCandle не выполняются. Наличие нулей — это строгое «НЕТ» для целей приложения.
Я не являюсь ни формально обученным программистом, ни экспертом, но являюсь любителем самообучения и определенно никогда раньше не пытался работать в многопоточности.
Поэтому я прошу вас, пожалуйста, указать мне, что я делаю неправильно, или, еще лучше, что я должен делать, чтобы это сработало.
Комментарии:
1. Это выглядит как идеальный случай для реактивного программирования.
2. Привет @GertArnold, я не слишком знаком с этой концепцией. Не могли бы вы пояснить, что вы имеете в виду?
Ответ №1:
Многопоточность и ядро EF несовместимы. Контекст ядра EF не является потокобезопасным. Вы должны создать новый контекст для каждого потока. Кроме того, обеспечение потокобезопасности вашего объекта — пустая трата времени.
Итак, схематично вы должны сделать следующее, и вы можете удалить блокировки с вашего объекта.
private void OnTick(Tick tickData)
{
using var ctx = new MyDbContext(...);
foreach (var instrument in ctx.IntradayInstruments.Where(i => i.InstrumentToken == tickData.InstrumentToken))
{
instrument.UpdateIntradayCandle(tickData);
}
ctx.SaveChanges();
}
Комментарии:
1. Привет @Svyatoslav Danyliv, как вы думаете, проблема в сохранении изменений? Если это так, то в базе данных должна отсутствовать целая свеча OHLC, а не только одно свойство без значения там, где оно должно его содержать. Я использовал блокировки объектов, думая, что свойство ‘IntradayCandle’ будет заменено новым экземпляром, пока в него записываются значения из другого потока. Кстати, основной контекст ef никогда не создавал никаких исключений при сохранении. Просто свойство ‘Open’ не получает своего значения.
2. @yoursvsr, объект не будет заменен. Контекст EF создает новую сущность, когда вы материализуете объекты, как вы делаете это int foreach . Он может кэшировать их в своем трекере изменений, но для каждого нового экземпляра контекста это будут новые объекты.
3. Пожалуйста, ознакомьтесь с методом AddNewIntradayCandle(), в котором я заменяю экземпляр новым каждые 5 м из другого потока, поступающего от объекта timer.
4. @yoursvsr, ничего не изменилось. Вы должны работать с одним контекстом на поток. Когда вы получаете объект из контекста — он принадлежит этому контексту. Похоже
IntradayCandles
, это свойство класса, но оно должно быть объектом, который вы запросили из контекста.5. Да, IntradayCandles — это список<IntradayCandle> и является свойством класса. Новые объекты создаются в коде и добавляются в список для сохранения каждые 5 м, после создания и обновления из тиков. Итак, я прошу прощения за то, что повторяюсь, в случае, если проблема связана с контекстом EF, из БД должна пропасть целая свеча OHLC, а не только одно свойство без значения там, где оно должно его содержать. Не могли бы вы пролить немного света?