У меня проблема, которую я полностью понимаю, но пытаюсь ее решить.
- У меня есть цикл обработки событий, который использует
async
/ await
. - Мне также нужно использовать транзакции, которые в течение времени жизни будутесть некоторые другие продолжения.
С учетом сказанного рассмотрите этот код.
public async Task UpdateItem(int mediaItemId)
{
using (var connection = _dataService.OpenDbConnection())
{
using (var transaction = connection.BeginTransaction())
{
var item = await connection.SingleByIdAsync<MediaItem>(mediaItemId);
item.Index++;
await connection.UpdateAsync(item);
transaction.Commit();
}
}
}
Вызывающая сторона этого метода происходит из цикла основного события.
Я начинаю с создания транзакции.Используя SqlLite, это фактически мьютекс уровня базы данных.Это означает, что во время выполнения транзакции другие вызовы BeginTransaction
будут блокироваться.
Теперь рассмотрим, что этот метод вызывается несколько раз в быстрой последовательности.После await
ing SingleByIdAsync
второй вызов попытается набрать BeginTransaction
, но он будет ждать там, пока первая транзакция не будет завершена.Ожидается, что за исключением того, что он заблокирует цикл основного события, предотвращая дальнейшие продолжения, оставляя первую транзакцию открытой.
Boom, deadlock.
Это будет решеноесли бы был IDbConnection.BeginTransactionAsync
, которого нет.Я мог бы перейти из цикла обработки событий и продолжить, как только транзакция будет успешно открыта.
Итак, рассмотрим следующее исправление:
public async Task UpdateItem(int mediaItemId)
{
using (var connection = _dataService.OpenDbConnection())
{
// Note that we are awaiting the opening of the transaction.
using (var transaction = await Task.Run(() => connection.BeginTransaction()))
{
var item = await connection.SingleByIdAsync<MediaItem>(mediaItemId);
item.Index++;
await connection.UpdateAsync(item);
transaction.Commit();
}
}
}
С учетом сказанного вы видите какой-либо вредпри открытии транзакции потока, который не открыл соединение с базой данных?Почему нет IDbConnection.BeginTransactionAsync
?await Task.Run(() => connection.BeginTransaction())
является приемлемым решением?