C # Печать и нарезание резьбы - PullRequest
7 голосов
/ 26 февраля 2010

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

 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);
    }
}

Ответы [ 3 ]

8 голосов
/ 26 февраля 2010

Попробуйте добавить элементы в очередь (например, Queue<Item>) и попросите 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<string> в моем коде выше на SizeQueue<PrintJob>, а также AddPrintItem(string item) на AddPrintJob(PrintJob job).

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

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

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

printer1.AddPrintJob(new PrintJob(labelText, numberOfCopies));
6 голосов
/ 26 февраля 2010

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

Есть все виды проблем с этим.

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

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

Первый - Использовать ThreadPool.QueueUserWorkItem (...)

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

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

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

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

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

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

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

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

0 голосов
/ 26 февраля 2010

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

Запустите такой метод в фоновом режиме:

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

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

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

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