Почему в C # очередь скремблирует данные в своих элементах? - PullRequest
3 голосов
/ 26 января 2009

Я полностью озадачен тем, как работает моя очередь. Я пытаюсь (и не могу) написать небольшое многопоточное приложение для сбора и отображения данных в C #.
После прочтения книги Албахари и использования Потребителя / Производителя шаблон, который он описывает, я получил большую часть его для работы, за исключением того, что мои данные, кажется, зашифрованы в очередь. Перед постановкой в ​​очередь поля в моем объекте имеют следующие значения

timeStamp = 6
данные [] = {4936, 9845, 24125, 44861}

После удаления данных данные выглядят как

timeStamp = 6
данные [] = {64791, 19466, 47772, 65405}

Я не понимаю, почему значения в поле данных изменяются после удаления из очереди? Я озадачен, поэтому подумал, что брошу это туда, чтобы посмотреть, сможет ли кто-нибудь указать мне правильное направление, чтобы исправить это, или указать мне другое направление, чтобы продолжить.


Соответствующий код


Пользовательский объект для хранения данных

Соответствующие объекты и поля. Класс sensorData - это отдельный класс, используемый для хранения моих вычислений.

public class sensorData
{
    public const int bufSize = 4;
    public UInt16[] data = new UInt16[4];
    public double TimeStamp = 0; 
    public int timeIndex = 0;
}

Следующие поля используются для настройки очереди и сигналов между потоками очереди и очереди.

EventWaitHandle wh = new AutoResetEvent(false);
Queue<sensorData> dataQ = new Queue<sensorData>();
object locker = new object();

Метод постановки в очередь / Тема

Это мой рабочий поток, он вычисляет четыре кривые синуса и ставит в очередь результат для обработки. Я также записываю результаты в файл, чтобы знать, что он рассчитал.

private void calculateAndEnqueueData(BackgroundWorker worker, DoWorkEventArgs e)
{
    int j = 0;
    double time = 0;
    double dist;
    UInt16[] intDist = new UInt16[sensorData.bufSize];
    UInt16 uint16Dist;

    // Frequencies of the four Sine curves
    double[] myFrequency = { 1, 2, 5, 10 };

    // Creates the output file.
    StreamWriter sw2 = File.CreateText("c:\\tmp\\QueuedDataTest.txt"); 

    // Main loop to calculate my Sine curves
    while (!worker.CancellationPending)
    {
        // Calculate four Sine curves
        for (int i = 0; i < collectedData.numberOfChannels; i++)
        {
            dist = Math.Abs(Math.Sin(2.0 * Math.PI * myFrequency[i] * time);
            uint16Dist = (UInt16)dist;
            intDist[i] = uint16Dist;
        }

        // Bundle the results and Enqueue them
        sensorData dat = new sensorData();
        dat.data = intDist;
        dat.TimeStamp = time;
        dat.timeIndex = j;

        lock (locker) dataQ.Enqueue(dat);
        wh.Set

        // Output results to file.
        sw2.Write(j.ToString() + ", ");
        foreach (UInt16 dd in dat.intData)
        {
            sw2.Write(dd.ToString() + ", ");
        }
        sw2.WriteLine();

        // Increments time and index.
        j++;
        time += 1 / collectedData.signalFrequency;

        Thread.Sleep(2);
    }
    // Clean up
    sw2.Close();
    lock (locker) dataQ.Enqueue(null);
    wh.Set();
    sw2.Close();
}

Пример строки в выходном файле QueuedDataTest.txt

6, 4936, 9845, 24125, 44861,

Метод данных удаления из очереди

Этот метод удаляет элементы из очереди и записывает их в файл. Пока в очереди не будет найден нулевой элемент, и в этот момент задание будет выполнено.

    private void dequeueDataMethod()
    {
        StreamWriter sw = File.CreateText("C:\\tmp\\DequeueDataTest.txt");

        while (true)
        {
            sensorData data = null;

            // Dequeue available element if any are there.
            lock (locker)
                if (dataQ.Count > 0)
                {
                    data = dataQ.Dequeue();
                    if (data == null)
                    {
                        sw.Close();
                        return;
                    }
                }

            // Check to see if an element was dequeued. If it was write it to file.
            if (data != null)
            {
                sw.Write(data.timeIndex.ToString() + ", ");
                foreach (UInt16 dd in data.data)
                    sw.Write(dd.ToString() + ", ");
                sw.WriteLine();
            }
            else
            {
                wh.WaitOne();
            }
        }

Вывод результата после удаления данных и записи их в DequeueDataTest.txt

6, 64791, 19466, 47772, 65405,


Обновления 1:

Местоположение замков в текущем коде.


Я отредактировал код, чтобы поместить блокировки вокруг записи данных в файл. Таким образом, блоки кода, которые у меня есть, выглядят следующим образом.

В методе CalculateAndEnqueueData () у меня есть

lock (locker) dataQ.Enqueue(dat);
wh.Set

lock(locker)
{
  sw2.Write(j.ToString() + ", ");
  foreach (UInt16 dd in dat.intData)
  {
     sw2.Write(dd.ToString() + ", ");
  }
  sw2.WriteLine();
}

В dequeueDataMethod () У меня есть две области с замками, первая здесь

lock(locker) 
    if (dataQ.Count > 0)
    {
       data = dataQ.Dequeue();
       if (data == null)
       {
           sw.Close();
           return;
        }
    }

, который, как я предполагаю, блокирует локер для кода в блоке if . Второй, где я пишу в файл здесь

lock (locker)
{
    sw.Write(data.timeIndex.ToString() + ", ");
    foreach (UInt16 dd in data.intData)
        sw.Write(dd.ToString() + ", ");
    sw.WriteLine();
    if (icnt > 10)
    {
        sw.Close();
        return;
    }
    this.label1.Text = dataQ.Count.ToString();
}

Это все из них.


Ответы [ 2 ]

9 голосов
/ 26 января 2009

Проблема связана с отсутствием синхронизации на StreamWriter, в который вы пишете. Порядок не является последовательным.

1 голос
/ 26 января 2009

Это потому, что вы пишете в один и тот же массив UInt16[] intDist снова и снова? Разве вы не должны использовать отдельные массивы для каждого sensorData объекта? (Кстати, sensorData.intData в вашем примере кода будет sensorData.data?)

РАЗЪЯСНЕНИЯ:

В calculateAndEnqueueData() создается только один массив intDist, поэтому разные экземпляры sensorData совместно используют один и тот же массив - это нормально, если добавление + запись и удаление + запись происходят на этапе блокировки; в противном случае некоторые точки данных могут отсутствовать / повторяться.

SUGGESTION:

Заполнение sensorData экземпляров напрямую, без использования массива intDist, в calculateAndEnqueueData():

    // create new sensorData instance
    sensorData dat = new sensorData();
    dat.TimeStamp = time;
    dat.timeIndex = j;

    // Calculate four Sine curves
    for (int i = 0; i < collectedData.numberOfChannels; i++)
    {
        dat.data[i] = (UInt16) Math.Abs(Math.Sin(2.0 * Math.PI * myFrequency[i] * time);
    }

    // enqueue
    lock (locker) dataQ.Enqueue(dat);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...