Многопоточные задачи, обращающиеся к статическому объекту со статическим дескриптором файла [duplicate]

Amit -

Не думаю, что вы можете добавить столбец в любом месте, но в конце таблицы после создания таблицы. Одним из решений может быть попытка:

  CREATE TABLE MY_TEMP_TABLE AS SELECT * FROM TABLE_TO_CHANGE;   

Отбросьте таблицу, в которую вы хотите добавить столбцы:

  DROP TABLE TABLE_TO_CHANGE;   

В тот момент вы можете перестроить существующую таблицу с нуля, добавив в столбцы, где хотите. Предположим, что для этого упражнения вы хотите добавить столбцы с именем «COL2 и COL3».

Теперь вставьте данные обратно в новую таблицу:

  INSERT INTO TABLE_TO_CHANGE (COL1  , COL2, COL3, COL4) SELECT COL1, 'Foo', 'Bar', COL4 FROM MY_TEMP_TABLE;   

Когда данные вставляются в вашу «новую-старую» таблицу, вы можете отбросить временную таблицу.

  DROP TABLE MY_TEMP_TABLE;   

Это часто бывает, когда я хочу добавить столбцы в определенном месте. Очевидно, что если это производственная он-лайн система, то это, вероятно, не практично, а только одна потенциальная идея.

-CJ

6
задан PaulG 28 August 2010 в 16:29
поделиться

4 ответа

Thread и QueueUserWorkItem являются самыми низкими доступными API для потоковой передачи . Я бы не использовал их, если бы у меня, наконец, не было другого выбора. Попробуйте класс Task для абстракции более высокого уровня. Подробнее см. В моем недавнем сообщении в блоге по теме .

Вы также можете использовать BlockingCollection<double> в качестве надлежащего очереди производителей / потребителей вместо того, чтобы пытаться для сборки один за другим с самыми низкими доступными API для синхронизации .

Повторное введение этих колес на удивление сложно. Я настоятельно рекомендую использовать классы, предназначенные для этого типа потребностей (Task и BlockingCollection, чтобы быть конкретными). Они встроены в платформу .NET 4.0, а доступны в качестве дополнения для .NET 3.5 .

9
ответ дан Stephen Cleary 17 August 2018 в 09:51
поделиться
  • код имеет автора как экземпляр var, но использует статический шкафчик. Если у вас было несколько экземпляров, записывающих разные файлы, нет причин, по которым они должны были бы использовать одну и ту же блокировку
  • в соответствующей заметке, поскольку у вас уже есть писатель (в качестве частного экземпляра var), вы можете использовать что для блокировки вместо использования отдельного объекта locker в этом случае - это делает вещи немного проще.

«Правильный ответ» действительно зависит от того, что вы ищете с точки зрения блокировки / блокирование. Например, проще всего пропустить промежуточную структуру данных, просто иметь метод WriteValues, чтобы каждый поток сообщал свои результаты и записывал их в файл. Что-то вроде:

StreamWriter writer = new StreamWriter("file");
public void WriteValues(IEnumerable<double> values)
{
    lock (writer)
    {
        foreach (var d in values)
        {
            writer.WriteLine(d);
        }
        writer.Flush();
    }
}

Конечно, это означает, что рабочие потоки сериализуются во время фаз «результатов отчета» - в зависимости от характеристик производительности, которые могут быть просто хорошими (5 минут для создания, 500 мс для записи , например).

На другом конце спектра вы должны будете, чтобы рабочие потоки записывались в структуру данных. Если вы в .NET 4, я бы рекомендовал использовать ConcurrentQueue вместо того, чтобы делать это самостоятельно.

Кроме того, вы можете захотеть сделать файл i / o в больших партиях, чем те, о которых сообщают рабочие потоки, поэтому вы можете просто писать в фоновом потоке на некоторой частоте. Этот конец спектра выглядит примерно так: вы должны удалить вызовы Console.WriteLine в реальном коде, они просто там, чтобы вы могли видеть, как он работает в действии)

public class ThreadSafeFileBuffer<T> : IDisposable
{
    private readonly StreamWriter m_writer;
    private readonly ConcurrentQueue<T> m_buffer = new ConcurrentQueue<T>();
    private readonly Timer m_timer;

    public ThreadSafeFileBuffer(string filePath, int flushPeriodInSeconds = 5)
    {
        m_writer = new StreamWriter(filePath);
        var flushPeriod = TimeSpan.FromSeconds(flushPeriodInSeconds);
        m_timer = new Timer(FlushBuffer, null, flushPeriod, flushPeriod);
    }

    public void AddResult(T result)
    {
        m_buffer.Enqueue(result);
        Console.WriteLine("Buffer is up to {0} elements", m_buffer.Count);
    }

    public void Dispose()
    {
        Console.WriteLine("Turning off timer");
        m_timer.Dispose();
        Console.WriteLine("Flushing final buffer output");
        FlushBuffer(); // flush anything left over in the buffer
        Console.WriteLine("Closing file");
        m_writer.Dispose();
    }

    /// <summary>
    /// Since this is only done by one thread at a time (almost always the background flush thread, but one time via Dispose), no need to lock
    /// </summary>
    /// <param name="unused"></param>
    private void FlushBuffer(object unused = null)
    {
        T current;
        while (m_buffer.TryDequeue(out current))
        {
            Console.WriteLine("Buffer is down to {0} elements", m_buffer.Count);
            m_writer.WriteLine(current);
        }
        m_writer.Flush();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var tempFile = Path.GetTempFileName();
        using (var resultsBuffer = new ThreadSafeFileBuffer<double>(tempFile))
        {
            Parallel.For(0, 100, i =>
            {
                // simulate some 'real work' by waiting for awhile
                var sleepTime = new Random().Next(10000);
                Console.WriteLine("Thread {0} doing work for {1} ms", Thread.CurrentThread.ManagedThreadId, sleepTime);
                Thread.Sleep(sleepTime);
                resultsBuffer.AddResult(Math.PI*i);
            });
        }
        foreach (var resultLine in File.ReadAllLines(tempFile))
        {
            Console.WriteLine("Line from result: {0}", resultLine);
        }
    }
}
6
ответ дан Daniel James Bryars 17 August 2018 в 09:51
поделиться

Итак, вы говорите, что хотите, чтобы поток потоков записывал данные в один файл с помощью StreamWriter? Легко. Просто заблокируйте объект StreamWriter.

Здесь будет создан 5 потоков. Каждый поток будет выполнять 5 «действий», и в конце каждого действия он будет писать 5 строк в файл с именем «файл».

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;

namespace ConsoleApplication1 {
    class Program {
        static void Main() {
            StreamWriter Writer = new StreamWriter("file");

            Action<int> ThreadProcedure = (i) => {
                // A thread may perform many actions and write out the result after each action
                // The outer loop here represents the multiple actions this thread will take
                for (int x = 0; x < 5; x++) {
                    // Here is where the thread would generate the data for this action
                    // Well simulate work time using a call to Sleep
                    Thread.Sleep(1000);
                    // After generating the data the thread needs to lock the Writer before using it.
                    lock (Writer) {
                        // Here we'll write a few lines to the Writer
                        for (int y = 0; y < 5; y++) {
                            Writer.WriteLine("Thread id = {0}; Action id = {1}; Line id = {2}", i, x, y);
                        }
                    }
                }
            };

            //Now that we have a delegate for the thread code lets make a few instances

            List<IAsyncResult> AsyncResultList = new List<IAsyncResult>();
            for (int w = 0; w < 5; w++) {
                AsyncResultList.Add(ThreadProcedure.BeginInvoke(w, null, null));
            }

            // Wait for all threads to complete
            foreach (IAsyncResult r in AsyncResultList) {
                r.AsyncWaitHandle.WaitOne();
            }

            // Flush/Close the writer so all data goes to disk
            Writer.Flush();
            Writer.Close();
        }
    }
}

Результатом должен быть файл «файл» с 125 строки в нем со всеми «действиями», выполняемыми одновременно, и результатом каждого действия, написанного синхронно с файлом.

4
ответ дан Lunatic Experimentalist 17 August 2018 в 09:51
поделиться
  • 1
    Вы не должны блокировать объекты, где вы не контролируете их реализацию, как это: что, если он внутренне блокирует себя в другом потоке? - вы должны создать новый Object для использования в качестве блокировки. – bdonlan 29 August 2010 в 02:37
  • 2
    Не используйте отдельный объект для блокировки. Блокировка объекта напрямую - единственный способ гарантировать, что все потоки, которые могут видеть объект, могут получить исключительную блокировку объекта. – Lunatic Experimentalist 29 August 2010 в 06:59
  • 3
    Довольно круто, именно то, что я хотел, спасибо! – user 29 August 2010 в 17:03
  • 4
    @LunaticExperimentalist: прямая блокировка объекта также не гарантирует, что все потоки получат эксклюзивную блокировку. Если разработчик забывает блокировать объект в определенном месте, он имеет неконтролируемый доступ, независимо от того, какой объект использует все остальные потоки для блокировки. – Dave C 3 November 2011 в 22:23

Код, который у вас там, тонко разбит - в частности, если первый элемент работает в очереди, он сразу же очистит (пустой) список значений до завершения, после чего ваш рабочий отправится и заполнит список (который в конечном итоге будет проигнорирован). Событие автоматического сброса также ничего не делает, поскольку ничто никогда не запрашивает или не ждет его состояния.

Кроме того, поскольку каждый поток использует другую блокировку , блокировки не имеют значения! При доступе к потоковому обозревателю необходимо обеспечить единую блокировку. Вам не нужна блокировка между кодом промывки и кодом генерации; вам просто нужно убедиться, что флеш работает после того, как закончится поколение.

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

2
ответ дан bdonlan 17 August 2018 в 09:51
поделиться
  • 1
    Как происходит промывка, если список пуст? – user 28 August 2010 в 16:51
  • 2
    Я объяснил это в своем ответе - работа в очереди WorkMethod может запускать до поток ThreadMethod. И он также может работать после. Вы не можете предсказать, что, потому что вы не задали никакого явного порядка здесь. – bdonlan 28 August 2010 в 17:03
Другие вопросы по тегам:

Похожие вопросы: