2017-02-18 4 views
0

Может ли кто-нибудь сказать мне, что лучший способ/правильный способ сделать это?
Я также использую WPF, а не консоль или ASP.NET.Понимание огня и забывание при использовании бесконечных циклов

Использование прослушивателя для приема клиентов и выделения нового «потока» для каждого клиента, который обрабатывает все операции ввода-вывода и исключения для этого клиента.

Способ 1: Огонь и забудьте, и просто выбросьте его в переменную, чтобы избавиться от предупреждения.

public static async Task Start(CancellationToken token) 
{ 
    m_server = TcpListener.Create(33777); 
    m_server.Start(); 
    running = true; 
    clientCount = 0; 

    // TODO: Add try... catch 
    while (!token.IsCancellationRequested) 
    { 
     var client = await m_server.AcceptTcpClientAsync().ConfigureAwait(false); 

     Client c = new Client(client); 

     var _ = HandleClientAsync(c); 
    } 
} 

Вот код клиента Handler:

public static async Task HandleClientAsync(Client c) 
{ 
    // TODO: add try...catch 
    while (c.connected) 
    { 
     string data = await c.reader.ReadLineAsync(); 

     // Now we will parse the data and update variables accordingly 
     // Just Regex and some parsing that updates variables 
     ParseAndUpdate(data); 
    } 
} 

Метод 2: То же самое ... но с Task.Run()

var _ = Task.Run(() => HandleClientAsync()); 

Метод 3: промежуточный, не асинхронном (сомните, что это хорошо. Должно быть Async полностью)
Но это, по крайней мере, избавляется от неряшливой линии, не используя переменную трюк, которая кажется грязной.

while (!token.IsCancellationRequested) 
{ 
    var client = await m_server.AcceptTcpClientAsync().ConfigureAwait(false); 

    Client c = new Client(client); 

    NonAsync(c); 
} 

public static void NonAsync(VClient vc) 
{ 
    Task.Run(() => HandleClientAsync(vc)); 
} 

Метод 4: Сделайте HandleClientAsync Асинхронной пустоты вместо асинхронной Task (очень плохо)

public static async Task HandleClientAsync(Client c) 
// Would change to 
public static async Void HandleClientAsync(Client c) 

Вопросов:

  • ли лучше использовать Task.Run() при выполнении огонь и забыть задачу?
  • Приходится ли вам использовать трюк var _ = FireAndForget(), чтобы сделать огонь и забыть? Я мог просто игнорировать предупреждение, но что-то не так.
  • Если бы я хотел обновить свой пользовательский интерфейс от клиента, как бы я это сделал? Я бы просто использовал диспетчера?

Спасибо, ребята

+0

Вы не должны использовать поток для обработки ввода/O-связанные задачи, IOCP не требуют потоков. Скручивание нитки - это отходы. – MickyD

+0

@MickyD, то как бы получить данные от клиента 3, если клиент 1 и 2 простаивают? Если я ожидаю данные от своих потоков, то я «блокирую», пока не получу эти данные. Если я использую 'WhenAll()', то я не могу получить данные от клиента 3 более одного раза. Однако я не вижу другого варианта. –

+0

Что заставляет вас думать, что 'await' является _" блокировкой "_? – MickyD

ответ

0

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

Моя рекомендация здесь? Используйте Thread class и управляйте ими сами. Таким образом, вы сохраняете пул потоков и накладные расходы для задач из картинки.

Добавление - Производитель Потребитель Модель

Еще один интересный вопрос для рассмотрения: Действительно ли вам нужен поток для каждого клиента? Потоки достаточно дороги для создания и обслуживания с точки зрения издержек памяти, и если взаимодействие с клиентом таково, что потоки клиентов тратят большую часть своего времени на то, чтобы что-то делать, то, возможно, модель producer consumer больше подходит для вашего использования дело.

Пример:

  • Клиент подключается на прослушивание поток, получает положить в очереди клиента
  • рабочий поток отвечает за проверку, чтобы убедиться, что клиенты должны что-нибудь приходит через эту очередь каждый так часто и проверяет - есть ли у клиента новое сообщение для обслуживания? Если это так, он обслуживает все сообщения, которые клиент имеет, а затем перемещается на

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

Если вы настаиваете

Если вы действительно любите то, что вы собираетесь, я предлагаю рефакторинга, что ты делал немного, так что вместо HandleClientAsync вы делаете что-то более похожее на CreateServiceForClient (с);

Это может быть синхронный метод, который возвращает что-то вроде ClientService. ClientService может затем создать задачу, которая делает то, что делает ваш HandleClientAsync, и сохранить эту задачу в качестве члена. Он также может предоставлять такие методы, как ClientService.WaitUntilEnd() и ClientService.Disconnect() (который может установить маркер отмены, также хранится в виде переменной-члена)

+0

Забавно. Я переформатировал код с асинхронным/ждущим кодом. Я только что много читал о том, как задачи лучше, поэтому я решил, что попробую. Но я нашел много этого, чтобы быть действительно запутанным, когда дело доходит до таких вещей. –

+0

В чем разница между использованием потока пула потоков и _ «Использовать класс Thread и управлять ими самостоятельно» _ для долговременной задачи? Во всяком случае, вы не должны использовать выделенный поток для операций с привязкой к вводу/выводу. IOCP не требует потоков – MickyD

+0

@MickyD Речь идет о ограничении ресурсов, зачем брать поток из пула, который предназначен для частого цикла, если вы собираетесь держать его навсегда - это сродни аренде книги и никогда не возвращает ее, когда вы должны купить его. И я согласен, что выделенный поток не нужен, это подразумевается в моем добавлении – Ben

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