Печать C# и поточная обработка

У меня есть потребность в пользователях смочь просканировать серию объектов и для каждой печати объекта от x количества маркировок. Я в настоящее время пытаюсь использовать второстепенного рабочего для выполнения этого, но я столкнулся с проблемой, где они сканируют объекты настолько быстро и существует столько маркировок для печати для каждого объекта фоновых дросселей рабочего. Это - то, как я генерирую фоновый рабочий поток для каждого сканирования, потому что конкуренция происходила, когда было большое количество маркировок быть распечатанным.

 private void RunPrintWorker()
    {
        if (printWorker.IsBusy)
        {
            printWorker = new BackgroundWorker();
            printWorker.DoWork += new DoWorkEventHandler(printWorker_DoWork);
            printWorker.RunWorkerAsync();
        }
        else
            printWorker.RunWorkerAsync();
    }

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

Спасибо.

Править: Спасибо все для предложений и чтения материала это должно действительно помочь. Порядок маркировки печатаются, действительно не имеет значения, так как они сканируют довольно быстро, и маркировки только печатаются к одному принтеру также. Я отмечу ответ после того, как я разбужу реализацию и выполнение.

Править: Остин, ниже - то, как у меня есть своя установка метода печати. Прежде чем я просто называл LabelPrinter. PrintLabels в моем методе RunPrintWorker. Теперь, когда я восстанавливаю это, я не могу выяснить, что передать в метод SizeQueue. Я должен передавать недавно созданный документ печати в него?

 public class LabelPrinter
{
    private int CurrentCount = 0;

    private List<int> _selectedRows = new List<int>();
    public List<int> SelectedRows
    {
        get { return _selectedRows; }
        set { _selectedRows = value; }
    }

    private string _selectedTemplate;
    public string SelectedTemplate
    {
        get { return _selectedTemplate; }
        set { _selectedTemplate = value; }
    }

    private string _templateDirectory = string.Empty;
    public string TemplateDirectory
    {
        get { return _templateDirectory; }
        set { _templateDirectory = value; }
    }

    public void PrintLabels(PrintDocument printDoc, PageSettings pgSettings, PrinterSettings printerSettings, List<int> selectedRows, string selectedTemplate, string templateDir)
    {
        this._selectedRows = selectedRows;
        this._selectedTemplate = selectedTemplate;
        this._templateDirectory = templateDir;

        printDoc.DefaultPageSettings = pgSettings;
        printDoc.PrinterSettings = printerSettings;

        printDoc.PrinterSettings.MaximumPage = selectedRows.Count();
        printDoc.DefaultPageSettings.PrinterSettings.ToPage = selectedRows.Count();
        printDoc.PrinterSettings.FromPage = 1;

        printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage);

        printDoc.Print();
    }

    private void printDoc_PrintPage(object sender, PrintPageEventArgs e)
    {
        CurrentCount = DrawLabel.DrawLabelsForPrinting(e, SelectedTemplate, SelectedRows, CurrentCount, TemplateDirectory);
    }
}
7
задан Nathan 26 February 2010 в 16:10
поделиться

3 ответа

Попробуйте добавить элементы в очередь (например, Queue ) и попросите BackgroundWorker обработать очередь.

РЕДАКТИРОВАТЬ: Добавление простого непроверенного кода, который может сработать для вас. Я бы инкапсулировал очередь печати с ее процессором и просто отправлял бы ей задания.

class SimpleLabelPrinter
{
    public bool KeepProcessing { get; set; }
    public IPrinter Printer { get; set; }

    public SimpleLabelPrinter(IPrinter printer)
    {
        Printer = printer;
    }


    /* For thread-safety use the SizeQueue from Marc Gravell (SO #5030228) */        
    SizeQueue<string> printQueue = new SizeQueue<string>();

    public void AddPrintItem(string item)
    {
        printQueue.Enqueue(item);
    }

    public void ProcessQueue()
    {
        KeepProcessing = true;

        while (KeepProcessing)
        {
            while (printQueue.Count > 0)
            {
                Printer.Print(printQueue.Dequeue());
            }

            Thread.CurrentThread.Join(2 * 1000); //2 secs
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        SimpleLabelPrinter printer1 = new SimpleLabelPrinter(...);
        SimpleLabelPrinter printer2 = new SimpleLabelPrinter(...);

        Thread printer1Thread = new Thread(printer1.ProcessQueue);
        Thread printer2Thread = new Thread(printer2.ProcessQueue);

        //...

        printer1.KeepProcessing = false;  //let the thread run its course...
        printer2.KeepProcessing = false;  //let the thread run its course...
    }
}

Реализация SizeQueue

РЕДАКТИРОВАТЬ 2: Обращение к обновленному коду в вопросе

Во-первых, я бы определил класс PrintJob , который содержит количество копий для печати и либо полный текст метки или достаточное количество данных для его получения (например, идентификаторы для запроса к БД). Это приведет к замене SizeQueue в моем коде выше на SizeQueue , а также AddPrintItem (строковый элемент) на AddPrintJob (Задание на печать) .

Во-вторых, я бы разделил ваш код LabelPrinter (возможно, создал бы этот интерфейс IPrinter) и передал бы его в конструктор моего SimpleLabelPrinter (это, возможно, не лучшее имя на данный момент, но я позволю вам справиться с этим).

Затем создайте LabelPrinter и SimpleLabelPrinter (например, printer1 в этом примере) везде, где это подходит для вашего приложения (в методах закрытия или «очистки» ваших приложений обязательно установите для KeepProcessing значение false, чтобы его поток заканчивается).

Теперь, когда вы сканируете элемент, вы отправляете его в SimpleLabelPrinter как:

printer1.AddPrintJob(new PrintJob(labelText, numberOfCopies));
8
ответ дан 6 December 2019 в 15:21
поделиться

По сути, вы говорите, что если принтер занят, перезапишите ваш объект printWorker другим воркером и запустите его. Тогда у вас нет ссылки на ваш старый рабочий объект и вы не выполняете никакой очистки.

С этим возникают всевозможные проблемы.

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

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

Сначала - используйте ThreadPool.QueueUserWorkItem (...)

ThreadPool.QueueUserWorkItem(new WaitCallback(o=>{printWorker_DoWork();}));

Это будет использовать пул потоков .net для постановки ваших задач в очередь и их обработки в пуле. (Размер пула будет автоматически увеличиваться до соответствующего количества потоков для обработки ваших запросов в очереди). Это не требует удаления или очистки, вы просто запускаете и забываете, хотя вам нужно знать, что рабочие элементы не обязательно будут обрабатываться в том же порядке, в котором вы их добавляете (вероятно, некоторые будут обрабатываться одновременно), поэтому, если вы заботитесь о порядке, это бесполезно.

Второй - Реализовать шаблон очереди потока производитель-потребитель . Для этого требуется синхронизированная очередь, в которую один поток или потоки (производитель) добавляют элементы, а другие потоки (потребители) удаляют и обрабатывают эти элементы. Если вы новичок в многопоточности, это может быть довольно сложно сделать правильно, поскольку вам нужно убедиться, что ваше чтение / запись в общую очередь правильно синхронизировано, чтобы порядок сохранялся и ничего не терялось.

Будьте осторожны с типом Queue , хотя его можно использовать, он сам по себе не является автоматически потокобезопасным. Убедитесь, что если вы его используете, вы добавляете свои собственные блокировки и синхронизацию.

Со страницы MSDN:

Потоковая безопасность любых членов экземпляра не гарантируется.

Queue ) может поддерживать несколько считывателей одновременно, пока коллекция не изменяется. Даже в этом случае перечисление через коллекцию по своей сути не является поточно-ориентированной процедурой. Чтобы гарантировать безопасность потоков во время перечисления, вы можете заблокировать коллекцию на протяжении всего перечисления. Чтобы разрешить доступ к коллекции нескольким потокам для чтения и записи, вы должны реализовать свою собственную синхронизацию.

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

6
ответ дан 6 December 2019 в 15:21
поделиться

Вам необходимо внедрить систему очередей. Взгляните на класс Queue и разместите в нем свои документы для печати. Раскрутите фоновый поток (не фоновый рабочий в этой ситуации) и попросите его периодически проверять очередь на наличие дополнительных элементов и распечатывать их, если это возможно.

Запустите такой метод в backgroundworker:

private void PrintLoop()
{
    while (true)
    {
        if (documents.Count > 0)
        {
            PrintDocument document = documents.Dequeue();
            document.Print();

        }
        else
        {
            Thread.Sleep(1000);
        }
    }
}

Создайте по одному BackgroundWorker для каждого принтера, и пусть каждый будет печатать на другом принтере.

0
ответ дан 6 December 2019 в 15:21
поделиться
Другие вопросы по тегам:

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