【问题标题】:Why does my code cause a deadlock?为什么我的代码会导致死锁?
【发布时间】:2016-12-12 09:51:56
【问题描述】:

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

我正在尝试从服务器请求聊天。在 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();
}

requestChats 在我的 UWP WebsocketManager 中看起来像这样:

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:

我在 cmets 中尝试了 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 - 信息:正在尝试发送数据...请求聊天

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

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

13.12.2016 07:55:32 - 信息:正在尝试发送数据...请求联系人

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

【问题讨论】:

  • 0x8000000E 错误表示您的异步操作没有完成但您尝试获取结果。
  • 您是否正在尝试存储/写入流并且多个线程正在访问同一个流。您需要放置一个信号量:await dataWriter.StoreAsync();
  • @AzharKhorasany 感谢您的建议。我试过了,但没有解决问题。我已经编辑了我的问题
  • @AzharKhorasany 实际上我发现我做错了,我应该已经使用 requestChats 中的信号量了......抱歉,现在试试
  • 你的 dataWriter 实例化在哪里?你不能在 SendData() 方法中实例化它吗?不应共享 DataWriter。它应该实例化、完成它的工作并被处理掉。

标签: c# asynchronous deadlock


【解决方案1】:

这段代码有几个潜在的问题,因为它显然没有考虑到并行线程。据我从您的代码中可以看出,您的直接问题是 dataWriter 在线程之间共享。想象一下,一个线程可能正在执行dataWriter.WriteString(...),而另一个线程正在执行dataWriter.StoreAsync(),您就会看到问题所在。您需要阅读有关并行编程的内容,并全面了解代码的执行方式。拥有像IsBusy 这样的标志可能在单线程程序中“工作”,一旦你有多个线程,它肯定会让你头疼。

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

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

【讨论】:

  • 问题是 IsBusy 标志不是我的代码,所以我真的不想改变它,除非真的有必要。我现在正在尝试清理我的 dataWriter 东西,然后看看它是否有效。谢谢
猜你喜欢
  • 2012-08-27
  • 1970-01-01
  • 2020-08-20
  • 2017-11-16
  • 2015-03-09
  • 2019-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多