Переменная область в многопотативной области, почему мое ссылка на объект теряется?
-
26-09-2019 - |
Вопрос
Кратко под одним производителем - один потребительский сценарий я использовал сметный объект для синхронизации и передачи данных и сообщений между производителем и потребителем. Общий буфер - это ConcurrentQueue
байтовых массивов. Для реализации циркулярного буфера и предотвратить фрагментацию кучи и частые замены объекта GC
Я использовал А. ConcurrentBag
байтовых массивов в качестве корзины для используемых байтовых массивов. ManualResetEventSlim
используется для синхронизации потоков. Иногда я теряю ссылку на байтовые массивы в моем коде. Ниже приведена упрощенная версия моего кода, если вам нужно больше деталей, но я думаю, что это обычная ошибка при работе с потоками.
MutableObject mutableObject = new MutableObject();
Producer producer = MutableObject.GetProducer();
Consumer consumer = MutableObject.GetConsumer();
Thread fork = new Thread(new ThreadStart(producer.Start));
// Forking execution path
fork.Start();
// Main thread goes here
consumer.Start();
class MutableObject()
{
private Producer m_producer;
private Consumer m_consumer;
private ConcurrentBag<byte[]> m_recycleBin = new ConcurrentBag<byte[]>();
private ConcurrentQueue<byte[]> m_sharedBuffer = new ConcurrentQueue<byte[]>();
public Producer GetProducer()
{
// Keep a reference to the mutable object
return new Producer(this);
}
// GetConsumer() method is just like GetProducer() method
public void GetEmptyBuffer(out byte[] buffer)
{
if (!m_recycleBin.TryTake(out buffer))
buffer = new byte[1024];
}
public bool Put(byte[] buffer)
{
m_sharedBuffer.Enqueue(buffer);
// Set ManualResetEventSlim for consumer
}
public bool Get(byte[] buffer) // Consumer calls this method in a loop
{
m_sharedBuffer.TryDequeue(out buffer);
// I save a reference to buffer here and pass it to recyclebin at next call like this: lastBuffer = buffer;
// This is because buffers are passing by refrence for I should wait until it would be used by consumer.
m_recycleBin.Add(lastBuffer);
// Set ManualResetEventSlim for producer
}
}
class Producer
{
private MutableObject m_mutableObject;
public Producer(MutableObject mutableObject)
{
m_mutableObject = mutableObject;
}
public void Start()
{
byte[] buffer;
while (true)
{
m_mutableObject.GetEmptyBuffer(out buffer);
m_mutableObject.Put(buffer);
}
}
}
Фактически GetEmptyBuffer()
Метод часто создает новые буферы, и хотя использованные буферы хранятся в корзине-корзине, счетчик корзины не воспитывает иногда!
Решение
public bool Get(byte[] buffer)
Это было бы одно очевидно, чтобы потерять ссылку. Этот метод не может на самом деле возвращать буфер, который был получен. Вам придется использовать реф ключевое слово, чтобы позволить ему вернуть массив. Трудно поверить, что настоящий код выглядит что-нибудь подобное, это просто не будет работать вообще. Есть много других красных флагов, ConcurrentBag имеет нить ассоциативность, материал теряется, если вы создадите потребительские потоки на лету. Вы не можете синхронизировать потребителя с производителем с помощью ManualResetEvent, он может сосчитать только 1.
В общем, эта оптимизация неуместна, если буферы не будут больше 85 КБ. Доверять сборщику мусора, это отличная работа, которая очень трудно улучшить.