C# 暂停线程直到服务器响应
C# Suspending Thread until Server responds
我正在尝试创建一个函数,当被调用时 returns 将信息返回给服务器上的调用者。我在这个函数中想要的是,它创建一个向服务器发出命令的线程,然后暂停自身,直到服务器用答案响应。
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 上 "await"——我需要等待某种回调函数(也就是我提到的恢复函数)——才能完成这项工作。
我不确定该怎么做
根据问题提供的所有信息,以下是您在使用 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
基础设施的魔力很可能会释放当前线程(假设实际上对服务器进行了调用)直到服务器 return是回应。
然后基础结构将使用现有的同步上下文 -如果你在一个 UI 线程上,例如 - 或者从线程池中获取一个线程 -如果不是- 运行 方法的其余部分。
您甚至可以通过简单地 returning a Task
和 AccountState
:
类型的结果来进一步减少方法的大小
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
一起使用的异步方法。
当然,我没有您实施的所有细节,但我希望您能了解需要做什么。神奇的 class 叫做 TaskCompletionSource。
它允许您做的是让您可以访问 Task
。您创建 TaskCompletionSource
class 的实例,将其保存在某处,发送命令并立即 return 该新实例的任务 属性。
从轮询线程获得结果后,您将获取 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());
}
}
}
我正在尝试创建一个函数,当被调用时 returns 将信息返回给服务器上的调用者。我在这个函数中想要的是,它创建一个向服务器发出命令的线程,然后暂停自身,直到服务器用答案响应。
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 上 "await"——我需要等待某种回调函数(也就是我提到的恢复函数)——才能完成这项工作。
我不确定该怎么做
根据问题提供的所有信息,以下是您在使用 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
基础设施的魔力很可能会释放当前线程(假设实际上对服务器进行了调用)直到服务器 return是回应。
然后基础结构将使用现有的同步上下文 -如果你在一个 UI 线程上,例如 - 或者从线程池中获取一个线程 -如果不是- 运行 方法的其余部分。
您甚至可以通过简单地 returning a Task
和 AccountState
:
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
一起使用的异步方法。
当然,我没有您实施的所有细节,但我希望您能了解需要做什么。神奇的 class 叫做 TaskCompletionSource。
它允许您做的是让您可以访问 Task
。您创建 TaskCompletionSource
class 的实例,将其保存在某处,发送命令并立即 return 该新实例的任务 属性。
从轮询线程获得结果后,您将获取 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());
}
}
}