#c# #multithreading #sync
#c# #многопоточность #синхронизация
Вопрос:
у меня есть простая служба Windows, которая запускается и запускает поток, который прослушивает / получает сердцебиение через tcp / ip. мне трудно найти способы синхронизации между получением информации из потока tcp и использованием этого значения для обновления чего-либо в основном потоке.
я пытаюсь использовать метод thread.sleep и продолжаю повторять его несколько раз, ожидая ответа от потока, а затем получая значение, но этот метод кажется немного изменчивым, поскольку метод иногда работает, а иногда нет.
итак, каков хороший способ синхронизации между этими двумя? по сути, я хочу запустить поток прослушивания tcp, получить определенное значение и обновить основную программу.
прилагаются функция приема и функция, которую я использовал для запуска потока. p.s: я совершенно новичок, когда дело доходит до tcp / ip и c #, поэтому любые комментарии к любой части кода или дизайна более чем приветствуются 🙂
public virtual void Receive()
{
string eventMessage = string.Empty;
int bytesRcvd = 0;
int totalBytesRcvd = 0;
byte[] byteBuffer = new byte[maxBufferSize];
NetworkStream listenStream;
try
{
if (client.Connected)
{
listenStream = client.GetStream();
}
else
{
return;
}
while (true)
{
//message that is slot in from the object will get sent here.
if (!string.IsNullOrEmpty(MessageToSend))
{
Send(MessageToSend);
MessageToSend = string.Empty;
}
// must convert it back and look for the delimiter, cannot wait for the three heartbeat to pass
string leftoverMsg = string.Empty;
bytesRcvd = listenStream.Read(byteBuffer, totalBytesRcvd, maxBufferSize - totalBytesRcvd);
totalBytesRcvd = bytesRcvd;
//if more than heart beat size, can process to see if it's a heartbeat and proceed to send
if (totalBytesRcvd > msgHeartbeatSize)
{
eventMessage = Encoding.ASCII.GetString(byteBuffer, 0, totalBytesRcvd);
ProcessMessage(eventMessage, ref leftoverMsg, ref totalBytesRcvd, ref byteBuffer);
}
}
}
catch (ThreadAbortException thEx)
{
//do nothing as main thread has aborted and waiting to close
logger.Info(Thread.CurrentThread.Name " is stopped. ");
}
catch (Exception exce)
{
bIsActive = false;
logger.Error(exce);
CleanUp();
}
finally
{
logger.Info(String.Format("Thread {0} Exiting. ", Thread.CurrentThread.Name));
}
}
public virtual void StartReceivingThread()
{
Thread thrReceive = new Thread(Receive);
try
{
if (!bIsActive amp;amp; Connect())
{
//NOTE: exception thrown by a thread can only be captured by that thread itself
//start a listen thread
//wait until heartbeat message is accepted
thrReceive.Name = "thr" serviceType.Name;
thrReceive.Start();
bIsActive = true;
//wait to get the heartbeat message
for (int i = 0; i < maxRetry; i )
{
Thread.Sleep(maxTimeOutValue);
if (bIsReceivingHeartbeat)
break;
}
//if nothing happens close the connection and try again
if (!bIsReceivingHeartbeat)
{
bIsActive = false;
CleanUp();
logger.Info("Closing receiver thread - " thrReceive.Name);
}
else
{
logger.Info("Starting receiver thread - " thrReceive.Name);
}
}
}
catch(Exception ex)
{
logger.Error(ex);
}
//finally
//{
// logger.Info("Exiting receiver thread - " thrReceive.Name);
//}
}
Ответ №1:
Я предполагаю bIsReceivingHeartbeat
bool
, что это переменная-член класса. Если значение, измененное в одном потоке (получателе), не отображается в другом потоке, это, скорее всего, связано с барьером памяти. Я говорю это из моего опыта работы с Java, но это, скорее всего, верно и в .net.
Попробуйте объявить переменные volatile
или использовать свойство и синхронизировать получатель и установщик:
private bool bIsReceivingHeartbeat;
public bool IsReceivingHeartbeat
{
[MethodImpl(MethodImplOptions.Synchronized)]
get { return bIsReceivingHeartbeat; }
[MethodImpl(MethodImplOptions.Synchronized)]
set { bIsReceivingHeartbeat = value; }
}
И в вызывающем коде:
if (!IsReceivingHeartbeat) ....
Я пишу из фона Java, но ситуация, скорее всего, аналогична
Комментарии:
1. да, это так. мм, есть какие-либо предостережения, о которых мне нужно остерегаться при использовании volatile?
2. Существует много вещей, сравнивающих volatile и synchronized. Обычно я предпочитаю последнее, но я не знаю почему. Одно хорошо документированное предостережение относится к такому использованию, как
volataileVairable
— два потока могут одновременно считывать одно и то же значение и устанавливать для него одно и то же значение, чистый результат, являющийся переменной, увеличивается только один раз, а не дважды. Мне любопытно узнать, исправило ли это вашу проблему или что-то еще является основной причиной.
Ответ №2:
(Похоже, вы также разместили этот код в refactormycode.com .)
В любом случае, вместо цикла с задержкой ожидания я рекомендую использовать объект события, который запускается с помощью кода, который задает IsReceivingHeartbeat . Смотрите классы ManualResetEvent и AutoResetEvent в MSDN.
Комментарии:
1. да, я это сделал, так как хотел получить какой-то ответ на этот дизайн / проблему, но я разместил это там, чтобы люди могли прокомментировать что-нибудь еще.