7
votes

C # Impression et enfilage

J'ai besoin que les utilisateurs puissent numériser une série d'éléments et pour chaque élément imprimer x nombre d'étiquettes. J'essaie actuellement d'utiliser un ouvrier d'arrière-plan pour y accomplir, mais j'ai rencontré un problème où ils numérisent des articles si rapides et il y a tellement d'étiquettes à imprimer pour chaque article. C'est ainsi que je générerai le fil de travail de l'arrière-plan pour chaque analyse, car la conflit était survenue lorsqu'il existait un grand nombre d'étiquettes être imprimés.

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


2 commentaires

J'ai trouvé des collectivités de fond pour être adéquat uniquement pour tirer un ou deux threads (j'ai également vu des chevauchements où un processus apporterait une autre). Vous voudrez peut-être essayer d'utiliser un système de file d'attente plutôt que de tirer un filetage chaque fois qu'une demande d'impression est effectuée. Ensuite, demandez à un travailleur d'arrière-plan saisir chaque article de la file d'attente, une à la fois jusqu'à épuisé.


Cela devrait être une réponse. C'est une très bonne suggestion.


3 Réponses :


8
votes

Essayez d'ajouter les éléments à une file d'attente (par exemple, file d'attente code>) et avez le processus d'arrêt du travail d'arrière-plan la file d'attente.

EDIT: STRUT> Ajout de quelques simples, code non testé pouvant fonctionner pour vous. Je encapsulerais la file d'attente d'impression avec son processeur et je viens d'envoyer des travaux informatiques. P> xxx pré>

Implémentation de TiazeQueue P>

EDIT 2: strong> Adressant le code mis à jour dans la question P>

Premièrement, je définirais une classe code> imprimante code> contenant le nombre de copies à imprimer et que le texte complet de l'étiquette ou suffisamment de données pour le dériver (comme IDS pour une requête de DB). Cela vous amènerait à remplacer TitryQueue code> dans mon code ci-dessus sur TialQueue code> ainsi que addPrintitem (élément de chaîne) code> to addprintjob (job) code>. p>

second, je garderais votre code de labelPritter séparé (peut-être créer cette interface IPrinter) et transmettre que dans le constructeur de mon simplelabelprinter (qui peut ne pas être le Meilleur nom à ce stade, mais je vous laisserai gérer cela). P>

Suivant, créez votre labelPrinter et SimplelabelPrinter (Dites Imprimante1 code> pour cet exemple), où qu'il convient à votre application ( Dans votre méthode de fermeture de vos applications ou de «nettoyage», assurez-vous de définir le processus de garde à une extrémité filette afin que son fil se termine). p>

Maintenant, lorsque vous numérisez un élément, vous l'envoyez à SimplelabelPrinter comme suit: P>

printer1.AddPrintJob(new PrintJob(labelText, numberOfCopies));


13 commentaires

Battez-moi à cela. Cependant, avoir plusieurs threads pourraient être une bonne idée.


J'ai construit un système pour cela et plusieurs threads d'impression peuvent causer plus de problèmes qu'ils ne résolvent - les utilisateurs finaux attendent des impressions dans l'ordre numérisé, par exemple. Si vous imprimez sur plusieurs imprimeurs, plusieurs threads d'impression sont logiques.


Seulement s'il a plus d'une imprimante :)


N'oubliez pas que la classe de la file d'attente n'est pas le fil sûr. Vous devez verrouiller / synchroniser toutes les actions en file d'attente. (Bien que vous puissiez avoir plusieurs lecteurs simultanés).


Je suis désolé mais je vais devoir downervote à cause de l'échantillon de code que vous avez ajouté. Ce code n'est pas le fil sûr. Vous ne prenez aucune serrure sur la file d'attente. Les documents MSDN pour la classe de file d'attente ( MSDN.MicRosoft.com/en-us/ Bibliothèque / 79777ey2C.aspx ) Énoncez que les opérations d'Enqueuse doivent être synchronisées. Je serai heureux de révoquer mon bowvote si vous corrigez ou supprimez l'échantillon de code.


@Austin: Bonne modification, Nice Référence, Down Vote supprimé et +1 pour répondre aux commentaires et produire un bon échantillon.


@Austin. Cela ne me laisserait pas +1, il faudra donc être un virtuel +1 mais j'ai enlevé le vote vers le bas.


@Nathan: Utilisez des classes séparées. Vous pouvez probablement aligner votre Imprimante CODE, mais conservez votre version de SimplelabelPrinter et TialQueue comme classes séparées.


D'où obtenez-vous IPrinter? Je ne trouve pas cette interface.


@Nathan: Je l'ai inventé. J'ai créé une interface similaire dans certains travaux récents pour prendre en charge plusieurs implémentations d'impression d'étiquettes.


Oh d'accord. Cela a du sens alors. Austin s'il vous plaît voir mon nouveau modifier dans la question principale.


Désolé, une chose de plus, tandis que (princitue.count> 0) est souligné car il n'y a pas de comptage pour PrintQueue. Dois-je mettre en œuvre IList pour résoudre ce problème?


@Nathan: Vous devrez l'exposer dans la mise en œuvre de TiazeQueue.



0
votes

Vous devez mettre en œuvre un système de file d'attente. Jetez un coup d'œil à la classe de la file d'attente et faites une file d'attente de vos imprimantes. Tournez un fil d'arrière-plan (pas un travailleur d'arrière-plan dans cette situation) et vérifiez périodiquement la file d'attente pour obtenir plus d'articles et imprimez-les s'il le peut.

Exécuter une méthode comme celle-ci dans le formidateur d'arrière-plan: xxx

Fabriquez un travailleur d'arrière-plan par imprimante et faites une imprimante à une autre imprimante.


0 commentaires

7
votes

Fondamentalement, ce que vous faites, c'est dire si l'imprimante est occupée, écrasez votre objet PrintWorker avec un autre travailleur et commencez celui-là. Vous n'avez alors aucune référence à votre ancien objet travailleur et ne faites pas de nettoyage.

Il y a toutes sortes de problèmes avec cela.

  • Les travailleurs de fond doivent être éliminés lorsque vous avez fini de prévenir les fuites.
  • Créer plus de travaux d'arrière-plan ne signifie pas que l'imprimante fonctionnera plus rapidement, cela signifie simplement que vous avez plus de travailleurs essayant d'accéder à l'imprimante en même temps.

    Ce que vous devez faire est de penser à faire la queue. Je dirais que vous avez deux options principales.

    Premier - utilisez Threadpool.QueueUeUserSerWorkItem (...) xxx

    Ceci utilisera le threadpool .NET pour faire la queue de vos tâches et les traiter sur la piscine. (La piscine sera une taille automatique sur un nombre approprié de threads pour gérer vos demandes en file d'attente). Cela ne nécessite aucune élimination ni nettoyage, vous venez de tirer et d'oublier, même si vous devez savoir que les éléments de travail ne sont pas garantis pour être traités dans le même ordre que vous les ajoutez (il est probable que certains seront traités simultanément), donc si Vous vous souciez de la commande, ce n'est pas bon.

    second - implémentez le modèle de filetage de fil de consommation . Cela nécessite une file d'attente synchronisée sur laquelle un fil ou des threads (le producteur) ajoute des éléments et d'autres threads (les consommateurs) suppriment et traitent ces éléments. Si vous êtes nouveau à enfiler, cela peut être assez délicat pour vous assurer que votre lecture / écriture à la file d'attente partagée est correctement synchronisée afin que la commande soit maintenue et que rien n'est perdu.

    Soyez prudent du Queue Type, bien que cela puisse être utilisé, il pourrait-il N'EST PAS AUTOMATIQUE SOIGNE SA SA en sécurité. Assurez-vous que si vous l'utilisez, vous ajoutez une partie de votre propre verrouillage et synchronisation.

    de la page MSDN:

    Tous les membres d'instance ne sont pas garantis pour être un fil de sécurité.

    Une file d'attente ) peut prendre en charge plusieurs lecteurs simultanément, tant que la collection n'est pas modifiée. Malgré tout, énumérer à travers une collection n'est intrinsèquement pas une procédure de fil-sécurité. Pour garantir la sécurité du thread lors de l'énumération, vous pouvez verrouiller la collection pendant toute l'énumération. Pour permettre à la collection d'accéder à plusieurs threads pour la lecture et l'écriture, vous devez mettre en œuvre votre propre synchronisation.

    Je recommanderais la première option la plus simple si vous ne vous souciez pas de la commande, mais si vous trouvez que vous avez besoin du contrôle de la file d'attente et de la transformation, ou la commande est importante, c'est-à-dire que lorsque vous pourrait se déplacer vers le modèle plus complexe - si vous suivez les liens au bas de l'article, vous trouverez exemples et Code source , vous pouvez également utiliser la classe de la file d'attente et assurez-vous d'obtenir votre droit de verrouillage.


4 commentaires

+1 pour la question de non-nettoyage. Mais le BGW utilise également le threepool, alors Wheeeuuserworkitem n'est pas vraiment différent.


@Henk, merci. Le fait qu'il ne nécessite pas de nettoyage, c'est une différence suffisante dans ce cas pour justifier le changement que je pense. Je dirais que le BGW est conçu comme une classe qui consiste à effectuer une seule action en arrière-plan. Il n'est pas vraiment conçu pour les scénarios impliquant une création de fil de plusieurs threads dans la manière dont Nathan l'utilisait.


Vous pouvez facilement avoir plusieurs BGW, par exemple si vous souhaitez utiliser des événements progressifsChanged et complété.


@Henk, oui vous pouvez, mais chaque BGW doit être attribué une seule action, c'est ce que je veux dire. Créer plusieurs fois de manière dynamique (bien que possible) n'est sûrement pas un excellent modèle car vous devez les nettoyer, ce qui signifie que vous devez tenir des références à toutes.