【问题标题】:C# Suspending Thread until Server respondsC# 暂停线程直到服务器响应
【发布时间】:2018-09-29 21:35:12
【问题描述】:

我正在尝试创建一个函数,该函数在被调用时会将信息返回给服务器上的调用者。我在这个函数中想要的是,它创建一个向服务器发出命令的线程,然后将自身挂起,直到服务器返回答案。

public AccountState GetAccount(string key)
{
  AccountState state = null;
  Thread t = new Thread(() =>
  {
    _connection.SomeCommandSentToServer(key);
    accountRequests.TryAdd(key, (Thread.CurrentThread, null));
    //Suspend current thread until ServerReponseHere is called
    Thread.CurrentThread.Suspend();
    //We have been resumed, value should be in accountRequests now
    accountRequests.TryRemove(key, out var item);
    state = item.AccountState;
  });
  t.Start();

  return state;
}


public ConcurrentDictionary<string, (Thread Thread, AccountState AccountState)> accountRequests = new ConcurrentDictionary<string, (Thread Thread, AccountState AccountState)>();

///Once server is done with processed command, call to this function made
public void ServerReponseHere(string key, AccountState state)
{
  accountRequests.TryGetValue(username, out var item);
  accountRequests.TryUpdate(username, (item.Thread, new AccountState()), item);
  item.Thread.Resume();
}

我的想法是,在另一个函数中,当服务器响应时,它会调用上面显示的 ResumeThread 函数。

C# 说 Suspend / Resume 是已弃用的函数,但是 -- 有什么更好的方法来做到这一点?


更新

关于“SomeCommandSentToServer”的说明——这只是通过 TCP 套接字向服务器发送命令。

在那个调用中,真正发生的只是传输到服务器。我正在使用一个使用 WinSock2.h 调用“Send()”的库——是的,我知道它是一个已弃用的库……但我正在使用的库需要它。

我有一个单独的线程来轮询来自服务器的输入。所以我没有办法在这个 SomeCommandSentToServer 上“等待”——我需要等待某种回调函数(也就是我提到的恢复函数)——才能完成这项工作。

我不确定该怎么做

【问题讨论】:

  • 看起来 async-await 是你的答案
  • 您能否更具体地说明如何使用 Tasks 使其工作?
  • 您如何获得您的AccountState_connection.SomeCommand 似乎没有返回任何内容。
  • 正如我所说的 -- SomeCommand -- 向服务器发出请求,然后服务器会在某个时候调用 ResumeThread() ---
  • async and await 正是针对这种情况制作的。

标签: c# multithreading async-await resume suspend


【解决方案1】:

有了问题中的所有可用信息,以下是您在使用 async / await 模式时应该达到的目标:

public async Task<AccountState> GetAccountAsync(string key)
{
    // The method SomeCommandSentToServerAsync must be changed to support async.
    AccountState state = await _connection.SomeCommandSentToServerAsync(key);

    return state;
}

您不太可能需要其他任何东西。我的意思是,我的意思是您将不必直接操作线程,将它们放入并发字典中并手动挂起或恢复它们,因为从维护的角度来看它看起来很糟糕;)

.NET 将处理线程部分,这意味着async 基础架构的魔力很可能会释放当前线程(假设实际调用了服务器),直到服务器返回响应。

然后基础架构将使用现有的同步上下文 -例如,如果您在 UI 线程上- 或者从线程池中获取线程 -如果不是-运行该方法的其余部分。

您甚至可以通过简单地返回带有AccountState 类型结果的Task 来进一步减小方法的大小:

public Task<AccountState> GetAccountAsync(string key)
{
    // The method SomeCommandSentToServerAsync must be changed to support async.
    return _connection.SomeCommandSentToServerAsync(key);
}

在这两个示例中,您也必须将调用者设为async

public async Task TheCallerAsync()
{
    // Grab the key from somewhere.
    string key = ...;

    var accountState = await <inst>.GetAccountAsync(key);

    //  Do something with the state.
    ...
}

将遗留方法转变为异步方法

现在,关于旧的 SomeCommandSentToServer 方法。有一种方法可以等待该遗留方法。是的,您可以将该方法转换为可与async / await 一起使用的异步方法。

当然,我不知道你的实现的所有细节,但我希望你能明白需要做什么。执行此操作的神奇课程称为TaskCompletionSource

它允许您做的是让您访问Task。您创建该 TaskCompletionSource 类的实例,将其保存在某处,发送命令并立即返回该新实例的 Task 属性。

从轮询线程中获得结果后,您将获取TaskCompletionSource 的实例,获取AccountState 并使用帐户状态调用SetResult。这会将任务标记为已完成并执行您要求的简历部分:)

这是一个想法:

    public Task<AccountState> SomeCommandSentToServerAsync(string key)
    {
        var taskCompletionSource = new TaskCompletionSource<AccountState>();

        //  Find a way to keep the task in some state somewhere
        //  so that you can get it the polling thread.

        //  Do the legacy WinSock Send() command.

        return taskCompletionSource.Task;
    }

    // This would be, I guess, your polling thread.
    // Again, I am sure it is not 100% accurate but 
    // it will hopefully give you an idea of where the key pieces must be.
    private void PollingThread()
    {
         while(must_still_poll)
         {
             //  Waits for some data to be available.

             //  Grabs the data.

             if(this_is_THE_response)
             {
                 // Get the response and built the account state somehow...

                 AccountState accountState = ...

                 // Key piece #1
                 // Grab the TaskCompletionSource instance somewhere.

                 // Key piece #2
                 // This is the magic line:
                 taskCompletionSource.SetResult(accountState);

                 // You can also do the following if something goes wrong:
                 // taskCompletionSource.SetException(new Exception());
             }
         }
    }

【讨论】:

  • 我认为您不理解 SomeCommandSentToServer 是一个网络调用——我在该函数中没有直接的方法来知道我什么时候需要等待,或者我什么时候知道我什么时候需要等待我正在恢复我的价值
  • 这个 SomeCommandSentToServer 在不同的线程上发出一些东西——然后向服务器发送该命令,——然后在轮询服务器输入的不同线程上获取命令,然后在“ServerResponseHere "
  • 一般网络调用自然是异步的。你告诉他们要做什么,以及他们完成后要运行什么代码。您通常甚至不需要使用线程来使这种异步工作。这正是一个完美的异步/等待用例。但是,没有足够的信息可供人们帮助您。此外,“你不太可能需要其他任何东西”取决于它运行的环境。它不会神奇地工作,例如,从控制台应用程序。您根本不需要暂停/恢复。寻找示例,这是 async/await 的常见用法。
  • @Flydog57 “不太可能你还需要其他任何东西”,我的意思是你不必操纵线程并将它们放入字典中 :)
  • 工作就像一个魅力!非常感谢@Kzrystof
猜你喜欢
  • 2014-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-10
  • 1970-01-01
  • 1970-01-01
  • 2017-11-26
  • 2017-06-24
相关资源
最近更新 更多