C # Приостановка потока, пока сервер не ответит - PullRequest
0 голосов
/ 30 сентября 2018

Я пытаюсь создать функцию, которая при вызове возвращает информацию вызывающей стороне, находящейся на сервере.В этой функции я хочу, чтобы она создала поток, который выдает команду серверу, а затем приостанавливает себя, пока сервер не ответит ответом.

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 - мне нужно было бы ожидать какой-то функции обратного вызова (так называемой функции возобновления, о которой я упоминал) - чтобы заставить это работать.

Яне знаете, как это сделать

1 Ответ

0 голосов
/ 30 сентября 2018

Со всей информацией, доступной из вопроса, вот что вы должны стремиться при использовании шаблона асинхронности / ожидания:

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, скорее всего, освободит текущий поток (при условии, что на самом деле сделан вызов серверу), пока сервер не вернет ответ.

ТогдаИнфраструктура будет либо использовать существующий контекст синхронизации - , если вы находитесь в потоке пользовательского интерфейса, например, - или захватить поток из пула потоков - , если не - для запуска остальной части метода.

Вы даже можете немного уменьшить размер метода, просто вернув Task с результатом типа AccountState:

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

В обоих примерах выhaver, чтобы сделать вызывающих async, а также:

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

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

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

Превращение устаревшего метода в асинхронный метод

Теперь, что касается legacу 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());
             }
         }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...