Проблема с синхронизацией вызовов token и syn c sql - PullRequest
0 голосов
/ 26 февраля 2020

(ОБНОВЛЕНИЕ: По просьбе Панагиотиса Канавоса я постараюсь быть более точным в том, чего я добиваюсь, поэтому я изменил исходный пост).

Мне нужно отменить длительный запрос с CancellationToken.

Скажем, у меня есть метод доступа к данным, похожий на этот:

void DoSomethingLongRunningOnSql(CancellationToken token){
   ...
}

... и я должен быть в состоянии гарантировать, что независимо от того, что такое время, если источник токена был отменен, этот метод не должен продолжать работать.

Вызывающая сторона может сделать следующее:

var source = new CancellationTokenSource();
var token = source.Token;

В потоке 1 это называется:

DoSomethingLongRunningOnSql(token); // Thread 1

В потоке 2, в то же время источник может быть отменен:

source.Cancel(); // Thread 2

В этом случае я хочу, чтобы длительный запрос просто останавливался независимо от того, является ли источник был отменен до начала или после запроса.

Я не могу использовать методы Asyn c классов Sql здесь, так как это должно работать в существующем контексте syn c. А переключение между syn c и asyn c склонно к проблемам с блокировкой.

Исходя из предложений Панагиотиса, следующий пример - лучший, который я мог придумать:

void DoSomethingLongRunningOnSql(CancellationToken token)
{
    // Avoid getting a connection from the pool if already cancelled.
    // This will ensure fast cancellation in the case that
    // connection pool is full and .Open() will hang.
    token.ThrowIfCancellationRequested();

    using (var conn = new SqlConnection("..."))
    {
        // It may in rare cases take some time to get a connection
        // from the connection pool, e.g. i max pool size has been reached
        conn.Open();

        // Throw if cancelled as close to the actual execution to lower
        // the risk of having the cancelation token canceled just before
        // executing the query, resulting in the query not being cancelled.
        token.ThrowIfCancellationRequested();

        var cmd = new SqlCommand("Some long running query", conn);

        // Support for cancellation
        token.Register(() => cmd.Cancel());

        // PROBLEM: If the other thread happened to call cancel at this,
        // specific time, the query will not be cancelled and this call 
        // will hang until the query is done.

        // Execute the sql command
        cmd.ExecuteNonQuery();
    }
}

Кто-нибудь знает, может ли это быть сделано на 100% безопасным способом или я должен просто полагаться на тот факт, что очень редко может произойти, чтобы отмена была вызвана в проблемное время c?

...