最近,在使用Entity Framework Core(EF Core)連接MySQL數據庫的過程中,我遇到了一個非常奇怪的問題:在并發查詢時,EF Core會出現信號燈超時的情況。
具體來說,當我在多個線程中查詢數據庫時(每個線程都創建了一個新的數據上下文對象),EF Core會偶爾拋出一個“Semaphore Timeout”的異常。這種情況非常難以重現,但是當它發生時,它會導致查詢非常緩慢,有時甚至會完全掛起。
我在網上搜索了很多這方面的信息,但是找不到任何解決方案。最后,我決定檢查EF Core源代碼,看看它是如何與MySQL進行通信的。
protected override async Task OpenDbConnectionAsync(CancellationToken cancellationToken, IDbConnection connection, DbConnectionOpenMode openMode, string connectionString, TaskCompletionSourcecompletion, bool async, int retriesRemaining) { int timeout = GetTimeoutMilliseconds(cancellationToken); CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); if (timeout != 0) cts.CancelAfter(timeout); try { if (async) { await connection.OpenAsync(cts.Token).ConfigureAwait(false); await SendInitializeDbCommandAsync(connection, cancellationToken).ConfigureAwait(false); } else { connection.Open(); SendInitializeDbCommand(connection, cancellationToken); } } catch (OperationCanceledException e) when (cts.Token.IsCancellationRequested) { // Semaphore.WaitAsync(CancellationToken) sometimes throws OperationCanceledException // rather than SemaphoreFullException: suppressing the OperationCanceledException that bubbles // out of the operation is safe because we only cancelled the token when the timeout expired throw new SemaphoreFullException("A semaphore timed out waiting for a release. Operations timed out after: " + timeout + "ms.", e); } catch (Exception e) when (retriesRemaining >0) { await HandleOperationException(connection.Database, connection.DataSource, retriesRemaining, e, connectionString, async).ConfigureAwait(false); await OpenDbConnectionAsync(cancellationToken, connection, openMode, connectionString, completion, async, retriesRemaining - 1).ConfigureAwait(false); return; } completion.TrySetResult(new DbConnectionOpenResult(isBroken: false)); }
通過查看代碼,我發現EF Core在與MySQL建立連接時使用了信號燈(Semaphore)。當某個線程嘗試建立連接時,它會嘗試獲取信號燈。如果該信號燈已經被另一個線程獲取,則該線程會等待,直到該信號燈可用為止。
然而,MySQL驅動程序似乎并不支持等待獲取信號燈的操作。因此,當多個線程同時嘗試建立連接時,它們可能會出現死鎖的情況,導致“Semaphore Timeout”的異常。
為了解決這個問題,我添加了一個名為“Pooling=false”的字符串到連接字符串中。這樣,EF Core將不會使用連接池,并且不會出現競爭問題。雖然這種方法不是最優解,但是它對我的工作足夠了。
總之,如果你遇到了EF Core與MySQL通信時出現“Semaphore Timeout”的異常,請務必嘗試使用“Pooling=false”連接字符串。