为什么我的代码会导致死锁?

时间:2016-12-12 09:51:56

标签: c# asynchronous deadlock

首先,我想说,如果这是一个糟糕的问题,我很抱歉。今天早上我有一个关于这个问题的问题并且可以解决一些问题,但我仍然陷入僵局,整个上午我都在努力解决这个问题。

我正在尝试从服务器请求聊天。在UWP中,我需要异步执行此操作。我的方法在大约每5次尝试时就会导致死锁,我正在运行以下代码。

第一次等待发生在OnNavigatedTo

public async void OnNavigatedTo(NavigationParameters parameters)
{
    IsLoadMoreItemsPossible = true;
    if (_first)
    {
        _first = false;
        await LoadDataTaskAsync();
    }
}

调用LoadDataTaskAsync

private async Task LoadDataTaskAsync()
{
    if (IsBusy)
    {
        return;
    }

    IsLoadMoreItemsPossible = false;
    IsBusy = true;
    IsFree = false;
    if (!CommonUtils.isInternetAvailable())
    {
        // load from local
    }
    await _remoteDataManager.requestChats();
}

_remoteDataManager.requestChats()为当前平台获取正确的方法:

public async Task requestChats()
{
    await _websocketManager.requestChats();
}
我的UWP WebsocketManager中的

requestChats如下所示:

public async Task requestChats()
{
    dataWriter.WriteString(WebsocketRequestFactory.Create(SocketEventsEnm.GET_CHATS));
    await SendData(dataWriter);
}

最后SendData,异常发生的地方:

private async Task SendData(DataWriter dataWriter)
{
    try
    {
        _evaLogger.Info("Trying to send data...");
        await dataWriter.StoreAsync(); // This is where the exception occurs
        _evaLogger.Info("Data was sent");
    }
    catch (Exception e)
    {
        _evaLogger.Error(e.Message, e);
    }
}

_evaLogger写入日志文件,该日志文件在发生死锁时记录:

  

12.12.2016 10:38:08 - 信息:Websocket已打开

     

12.12.2016 10:38:08 - 信息:尝试发送数据......

     

12.12.2016 10:38:08 - 错误:在意外时间调用了一个方法。 (来自HRESULT的异常:0x8000000E)

     

12.12.2016 10:38:09 - 信息:尝试发送数据......

     

12.12.2016 10:38:09 - 信息:已发送数据

很抱歉这个很长的问题,我试图只显示死锁的相关代码。

编辑1:

我在评论中尝试了Azhar Khorasany的建议,以便在访问流时使用信号量:

public class AsyncLock
{
    public readonly AsyncSemaphore m_semaphore;
    private readonly Task<Releaser> m_releaser;

    public AsyncLock()
    {
        m_semaphore = new AsyncSemaphore(1);
        m_releaser = Task.FromResult(new Releaser(this));
    }

    public Task<Releaser> LockAsync()
    {
        var wait = m_semaphore.WaitAsync();
        return wait.IsCompleted ?
            m_releaser :
            wait.ContinueWith((_, state) => new Releaser((AsyncLock)state),
                this, CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
    }
}

requestChats方法:

private static readonly AsyncLock m_lock = new AsyncLock();

public async Task requestChats()
{
    using (await m_lock.LockAsync())
    {
        using (DataWriter dataWriter = new DataWriter(messageWebsocket.OutputStream))
        {
            _evaLogger.Info("dataWriter initialized requestChats.");
            dataWriter.WriteString(WebsocketRequestFactory.Create(SocketEventsEnm.GET_CHATS));
            await SendData(dataWriter, "requestChats");
        }
    }
    _evaLogger.Info("dataWriter disposed requestChats.");
}

不幸的是,它没有解决死锁问题:

  

13.12.2016 07:55:31 - 信息:Websocket已打开

     

13.12.2016 07:55:32 - 信息:dataWriter初始化了requestChats。

     

13.12.2016 07:55:32 - 信息:尝试发送数据... requestChats

     

13.12.2016 07:55:32 - 错误:在意外时间调用了一个方法。 (来自HRESULT的异常:0x8000000E)

     

13.12.2016 07:55:32 - 信息:dataWriter处理了requestChats。

     

13.12.2016 07:55:32 - 信息:尝试发送数据... requestContacts

     

13.12.2016 07:55:32 - 信息:dataWriter初始化了requestContacts。

1 个答案:

答案 0 :(得分:1)

此代码中存在几个潜在问题,因为它显然不是用并行线程编写的。从我的代码中我可以看出,你的直接问题是线程之间共享dataWriter。想象一下,当另一个线程正在执行dataWriter.WriteString(...)时,一个线程可能正在执行dataWriter.StoreAsync()并且您看到了问题。您需要了解并行编程,并充分了解代码的执行方式。像IsBusy这样的标志可以&#34;工作&#34;在一个单线程程序中,只要你有多个线程,它就会让你头疼。

快速建议您的代码: 删除dataWriter的全局声明并更改以下代码:

public async Task requestChats()
{
    /* declare dataWriter here so it becomes an instance variable*/
    dataWriter.WriteString(WebsocketRequestFactory.Create(SocketEventsEnm.GET_CHATS));
await SendData(dataWriter);