2015-09-10 7 views
1

Я общаюсь с другим процессом через именованные каналы. Сервер трубопроводов реализован на C#, а клиент написан на C. Сервер - это приложение WPF.Висячие с WaitForConnectionAsync() на трубе

Мне нужно создать NamedPipeServerStream и подождать (синхронно) до 1 секунды для подключения клиента. И тогда мне нужно знать, подключен ли клиент.

Как единственный способ NamedPipeServerStream «s, чтобы отменить/таймаут ожидания для клиента подключения осуществляется через его асинхронный WaitForConnectionAsync метод - которая принимает CancellationToken - Я реализовал то, что я считаю, является синхронным ждать так:

public bool WaitOneSecondForClientConnect() 
{ 
    bool result = false; 
    try 
    { 
     result = WaitForConnectionAsyncSyncWrapper().Result; 
    } 
    catch (AggregateException e) 
    { 
     log.Write("Error waiting for pipe client connect: " + e.InnerException.Message); 
    } 
    return result; 
} 

private async Task<bool> WaitForConnectionAsyncSyncWrapper() 
{ 
    CancellationTokenSource cts = new CancellationTokenSource(1000); 
    await pipe.WaitForConnectionAsync(cts.Token); 
    return pipe.IsConnected; 
} 

Труба определяется следующим образом: NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1); Функция WaitOneSecondForClientConnect() работает на потоке пользовательского интерфейса.

Что должно сделать синхронным доступ к функции WaitForConnectionAsyncSyncWrapper() функции async Result на Task<bool>, она возвращается. Чтобы получить доступ к результату, функция async должна быть полностью возвращена, и она не может выполнить строку return pipe.IsConnected до тех пор, пока функция не будет возобновлена ​​после завершения await pipe.WaitForConnectionAsync(cts.Token);. По крайней мере, это мое понимание.

Итак, проблема: хотя клиентская программа говорит, что она открыла трубку моего сервера (которая уже была создана, конечно, до того, как код выше выполнит), WaitOneSecondForClientConnect() никогда не возвращается. Если я прорвусь на сервер, он находится в этой строке: result = WaitForConnectionAsyncSyncWrapper().Result;.

Так что я предполагаю, что он ждет результат Целевой быть доступным, которое должно быть значение pipe.IsConnected, если клиент подключен в течение 1 секунды, или он должен бросить AggregateException, когда я к нему доступ, если await завершена, поскольку маркер был отменен (через 1 секунду). Но он просто висит полностью.

С другой стороны, если я отменяю токен перед его началом, например. поставив Thread.Sleep(2000); прямо перед вызовом await pipe.WaitForConnectionAsync(cts.Token);, то соединение успешно отменен (я думаю, что это даже не пытается начать, потому что маркер уже отменен) - доступ к Result собственности бросает AggregateException и т.д ...

Несколько замечаний.

  • Если я заменяю содержание WaitOneSecondForClientConnect() с стандартного синхронного pipe.WaitForConnection();, он работает каждый раз - т.е. клиент соединяется, и функция возвращает.
  • В тестовой программе, которую я написал, чтобы сначала получить эту работу с асинхронным/синхронизирующим файлом, соединение в этом синхронно-асинхронном режиме работает каждый раз. Это консольная программа, в отличие от моей реальной программы, которая представляет собой WPF. Соответствующий код тестовой программы приведен ниже.
  • Код, указанный выше, фактически работал пару раз и не мог быть 30-40 раз.
  • Если мой клиент не открывает мою трубу, мой «настоящий» код все еще зависает, тогда как мой тестовый код ждет указанный период времени, а затем печатает сообщение «Connection failed». как и ожидалось (см. ниже) - это именно то поведение, которое должно происходить в моем реальном коде.

тест код, который работает:

var pipe = new NamedPipeServerStream("SemiUsefulPipe_" + pid.ToString() + "ctest", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1); 

// ... dll containing pipe client is injected in client process at this point. 

try 
{ 
    var result = ConnectAsync(pipe).Result; 
} 
catch (AggregateException) 
{ 
    Console.WriteLine("Connection failed."); 
} 

...

private static async Task<bool> ConnectAsync(NamedPipeServerStream pipe) 
{ 
    CancellationTokenSource cts = new CancellationTokenSource(1000); 
    await pipe.WaitForConnectionAsync(cts.Token); 
    return pipe.IsConnected; 
} 
+0

Вы не используете функцию async/wait правильно –

+0

Возможно, нет - не могли бы вы разработать? – eurotrash

+0

Ваш вопрос слишком длинный для меня, чтобы все это принять и сформулировать ответ –

ответ

2

Вы не можете смешивать асинхронные и не асинхронные методы, как это. Что происходит, так это то, что ваш метод WaitOneSecondForClientConnect ждет завершения метода WaitForConnectionAsyncSyncWrapper. Но этот парень нуждается в том, чтобы его вызывающая нить была бесплатной, чтобы он мог увлажнить исходный контекст. Итак, вы только что создали тупик. Для получения дополнительной информации см. https://msdn.microsoft.com/en-us/magazine/jj991977.aspx.

Вместо этого вам нужно пройти асинхронно до конца.

public async Task<bool> WaitOneSecondForClientConnect() 
{ 
    bool result = false; 
    try 
    { 
     result = await WaitForConnectionAsyncSyncWrapper(); 
    } 
    catch (Exception e) 
    { 
     log.Write("Error waiting for pipe client connect: " + e.Message); 
    } 
    return result; 
} 
+0

Brilliant, эта ссылка описывает мою проблему для T (в том числе, почему она работает в моем тестовом коде, а не в моем графическом приложении). Теперь почти наверняка лучший или «более правильный» способ сделать это, поскольку я новичок в async/await; поэтому для того, чтобы сделать синхронный метод, я оставляю свой код как есть, но предотвращая тупик, добавляя 'ConfigureAwait (false)' в конец 'await pipe.WaitForConnectionAsync (cts.Token)', поэтому он выполняет остаток на пул потоков, и он работает! – eurotrash

Смежные вопросы