#c# #.net #design-patterns
#c# #.net #шаблоны проектирования
Вопрос:
Любопытная картина сбоев, связанных с потоками, и их своевременное завершение. Замечаю, что этот шаблон довольно часто повторяется в продукте.
Допустим, у вас есть отказоустойчивый процесс — сетевое подключение — также используйте SSL, или соединение и запрос WMI, или соединение с базой данных и запрос и т.д.
Ваше сетевое клиентское соединение при сбое подождет некоторое время (скажем, 5 минут), а затем попытается подключиться повторно.
Иногда, когда это происходит, вы также можете захотеть остановить сеть, это приводит ко второму потоку, сигнализирующему о том, что он останавливается и выполняет некоторое завершение работы.
Если ожидает отказоустойчивый процесс, то его достаточно легко отключить, заставив его ожидать события, управляемого другим потоком.
Как бы вы остановили функцию Connect на полпути, потому что она требует длительных / дорогостоящих процессов? Как управлять шагами в Connect()
после / во время Cancel()
вызова, на данный момент возможно вызвать Cancel / Cleanup, пока Connect находится в процессе создания нового SslStream и, таким образом, успешно завершить Connect с неполным состоянием — m_network, установленным для нового объекта.
private TcpClient m_network = new TcpClient();
private ManualResetEvent m_closing = new ManualResetEvent(true);
private void Connect()
{
try
{
m_network.Connect(m_config.Address.Host, m_config.Address.Port);
SslStream sslStream = new SslStream(m_network.GetStream(), true, ValidateServerCertificate, ClientCertificateSelection );
X509CertificateCollection clientCertificates = GetClientCertificates();
sslStream.AuthenticateAsClient(m_config.Address.Host, clientCertificates, SslProtocols.Default, false );
// do more stuff in a new thread
}
catch (System.Exception ex)
{ Reset(); }
}
public void BeginExecution()
{
ThreadPool.QueueUserWorkItem(delegate
{
m_closing.Reset();
Connect();
});
}
public void Cancel()
{
m_closing.Set();
Cleanup();
}
private void Cleanup()
{
TcpClient tempClient = m_network;
m_network = new TcpClient();
if (tempClient.Connected)
{
tempClient.Close();
}
}
void Reset(){
if (m_closing.WaitOne(0))
return;
Cleanup();
if (m_closing.WaitOne(TimeSpan.FromSeconds(300)))
return;
ThreadPool.QueueUserWorkItem(delegate { Connect(); });
}
Блокировка подключения или использование переменной локальной сети сводит на нет цель попытки отключить ее раньше.
Кажется некрасивым продолжать проверять событие после каждого действия при подключении, а затем вызывать Cleanup.
Комментарии:
1. есть ли шанс использовать F # здесь? Как насчет асинхронного CTP?
2. Чтобы получить более подробный ответ на свой вопрос, рассмотрите возможность повторного пометки его.
3. @GregC: спасибо, асинхронный CTP выглядит интересным, хотя и недоступен в framework 2? пока нет шансов на F #.
4. Вы хотите сказать, что ограничены .NET framework 2.0? Если бы у вас был 4.0, вы могли бы использовать Task Parallel Library и ContinueWith для достижения того же эффекта, что и асинхронный блок в CTP: единственная разница в том, что код пришлось бы писать в обратном направлении.
Ответ №1:
Хорошая особенность асинхронного блока в F # или асинхронного CTP для C # заключается в том, что вы можете экстернализировать обработку потоков отмены и таймаута. Вы можете проделать аналогичные вещи с TPL и продолжениями, если необходимо.