Переменная область в многопоточности, почему моя ссылка на объект потеряна? - PullRequest
0 голосов
/ 25 октября 2010

Вкратце, по сценарию с одним производителем - одним потребителем, я использовал изменяемый объект для синхронизации и передачи данных и сообщений между производителем и потребителем.Общий буфер - это 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() часто создает новые буферы, и хотя используемые буферыхранящиеся в корзине, количество корзин иногда не увеличивается!

1 Ответ

2 голосов
/ 26 октября 2010
public bool Get(byte[] buffer)

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...