Чё-т торможу, не могу вкурить как написать правильно.
public class MainClass : IDisposable
{
private Thread _th;
private UnmanagerResource _res;
private object _syncObj = new object();
public MainClass()
{
_res = new UnmanagerResource();
StartListen();
}
private void StartListen()
{
_th = new Thread(() =>
{
while (true)
{
// ---------- как (и надо ли?) убедиться здесь, что ресурс доступен (Dispose к нему не был вызван и он не null)?
var data = _res.Read();
// do someting with the data
}
});
_th.Start();
}
private void StopListen()
{
_th.Abort();
_th = null;
}
public void Dispose()
{
lock (_syncObj)
{
if (_res != null)
{
StopListen(); // гарантирует ли Thread.Abort, что строка с _res.Read не будет вызвана при _res = null?
_res.Dispose();
_res = null;
}
}
}
}
public class UnmanagerResource : IDisposable
{
public string Read()
{
int data = (new Random()).Next(5000, 10000);
Thread.Sleep(data);
return data.ToString();
}
public void Dispose()
{
}
}
Здравствуйте, Константин, Спасибо за ответ!
К>1. Убивать поток вызовом "Abort()" неправильно.
да, эт знаю.
К>Если ваш UnmanagerResource это сокет, можете закрыть сокет, сразу после этого Read() выкинет исключение, вам нужно его поймать и написать return из вашего while(true) в треде.
нет, у меня это WebSphere MQ Queue. Как оказалось, если вошло в Read, то Close виснет вообще.
К>Если не сокет — можете например создать ManualResetEventSlim, и вместо while(true) написать while(!m_shouldStopEvent.IsSet), но тогда вам нужно или гарантировать шо Read() регулярно шо-то возвращает, или прикручивать к Read() поддержку cancellation или таймаутов.
не вполне понял. если надо что-то докручивать в самом UnmanagedResource, то опять же — он у меня настолько неуправляемый, что даже переписыванию не поддаётся.
в общем, в итоге (из-за зависания на Close) пришлось устанавливать тайм-аут для этой очереди и после каждого тайм-аута опять вставать на прослушку. соответственно в перерывах теперь можно проверять на надобность прерывания.
_listenThread = new Thread(() =>
{
try
{
while (true)
{
MQQueue qIn = null;
try
{
qIn = _managerIn.AccessQueue(_inQueue, MQC.MQOO_INPUT_AS_Q_DEF | +MQC.MQOO_FAIL_IF_QUIESCING);
var msg = new MQMessage();
qIn.Get(msg, options);
onMessageArrived(msg);
}
catch (MQException ex)
{
if (ex.ReasonCode != 2033)
{
throw ex;
}
}
finally
{
if (qIn != null)
{
qIn.Close();
}
}
if (_needToStop)
break;
}
}
catch (Exception ex)
{
if (!(ex is ThreadAbortException))
{
onListeningFailed(ex);
}
}
});
теперь тот же вопрос касательно _managerIn — он у меня в Dispose диспозится и обнуляется. Конечно StopListen вызывается до этого и вероятно никогда ничего плохого не случится (т.е. к моменту вызова _managerIn.Access менеджер всегда будет not null), но не покидает ощущение зыбкости. А lock ставить внутри while(true) почему-то совсем не хочется.
К>2. У вас нет проверки на то что StartListen будет вызван несколько раз. В этом случае у вас будет несколько тредов, и скорее всего всё сломается.
да вроде там только из конструктора вызываю.
в любом случае, это был просто пример кода — чтобы вопрос задать. как вы понимаете, вопрос был по другой части кода.
К>3. Использование Thread + blocking IO для подобных задач вообще редко хорошая идея. Смотрите лучше в сторону async-await.
я пока на четвёртом дотнете сижу (никак не вылезу). может и можно как-то прикрутить, но не до сук.