Exécuter le travail sur un fil spécifique

J’aimerais avoir un thread spécifique, une queue pour les tâches et des tâches de processus dans ce thread séparé. L’application créerait des tâches en fonction de l’utilisation des utilisateurs et les placerait en queue. Ensuite, le thread séparé traite les tâches. Il est essentiel de maintenir le thread actif et de l’utiliser pour traiter les tâches en queue, même si la queue est vide.

J’ai essayé plusieurs implémentations de TaskScheduler avec BlockingCollection et limité la concurrence à un seul thread, mais il semble que le thread soit supprimé lorsque la queue est vide et que la tâche est traitée par un autre thread.

Pouvez-vous s’il vous plaît au moins me référer à certaines sources comment atteindre cet objective?

tl; dr Essayer de limiter un thread spécifique au traitement de tâches ajoutées dynamicment à la queue.

Edit1:

Il s’agit d’une application Web expérimentale utilisant WCF et .NET Framework 4.6. Dans la bibliothèque WCF, j’essaie d’implémenter ce comportement avec des tâches de traitement d’un seul thread. Ce fil doit initier le prolog à l’aide de la bibliothèque de DLL externe, puis fonctionner avec le prolog. Si un autre thread est utilisé dans le processus, la bibliothèque lève une AccessViolationException . J’ai fait des recherches et c’est très probablement à cause des threads mal gérés dans cette bibliothèque. J’avais une implémentation où j’avais des cadenas partout et cela fonctionnait J’essaie maintenant de réimplémenter et de le rendre asynchrone afin que je ne bloque pas le thread principal avec le locking.

Je ne suis pas devant mon ordinateur mais je fournis un code quand je rentre chez moi plus tard aujourd’hui.

Votre approche semble bonne, alors vous venez probablement de commettre une petite erreur stupide.

Il est en fait assez facile de créer un TaskScheduler personnalisé et simple. Pour votre cas:

 void Main() { var cts = new CancellationTokenSource(); var myTs = new SingleThreadTaskScheduler(cts.Token); myTs.Schedule(() => { Print("Init start"); Thread.Sleep(1000); Print("Init done"); }); myTs.Schedule(() => Print("Work 1")); myTs.Schedule(() => Print("Work 2")); myTs.Schedule(() => Print("Work 3")); var lastOne = myTs.Schedule(() => Print("Work 4")); Print("Starting TS"); myTs.Start(); // Wait for all of them to complete... lastOne.GetAwaiter().GetResult(); Thread.Sleep(1000); // And try to schedule another myTs.Schedule(() => Print("After emptied")).GetAwaiter().GetResult(); // And shutdown; it's also pretty useful to have the // TaskScheduler return a "complete task" to await myTs.Complete(); Print("On main thread again"); } void Print(ssortingng str) { Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, str); Thread.Sleep(100); } public sealed class SingleThreadTaskScheduler : TaskScheduler { [ThreadStatic] private static bool isExecuting; private readonly CancellationToken cancellationToken; private readonly BlockingCollection taskQueue; public SingleThreadTaskScheduler(CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; this.taskQueue = new BlockingCollection(); } public void Start() { new Thread(RunOnCurrentThread) { Name = "STTS Thread" }.Start(); } // Just a helper for the sample code public Task Schedule(Action action) { return Task.Factory.StartNew ( action, CancellationToken.None, TaskCreationOptions.None, this ); } // You can have this public if you want - just make sure to hide it private void RunOnCurrentThread() { isExecuting = true; try { foreach (var task in taskQueue.GetConsumingEnumerable(cancellationToken)) { TryExecuteTask(task); } } catch (OperationCanceledException) { } finally { isExecuting = false; } } // Signaling this allows the task scheduler to finish after all tasks complete public void Complete() { taskQueue.CompleteAdding(); } protected override IEnumerable GetScheduledTasks() { return null; } protected override void QueueTask(Task task) { try { taskQueue.Add(task, cancellationToken); } catch (OperationCanceledException) { } } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { // We'd need to remove the task from queue if it was already queued. // That would be too hard. if (taskWasPreviouslyQueued) return false; return isExecuting && TryExecuteTask(task); } } 

Il est assez facile de modifier cela pour vous donner un contrôle total sur l’endroit où le planificateur de tâches exécute réellement la tâche. En fait, je l’ai adapté à partir d’un planificateur de tâches précédent que j’avais utilisé et qui avait simplement la méthode RunOnCurrentThread publique.

Pour votre cas, où vous voulez toujours vous en tenir à un seul thread, l’approche dans SingleThreadTaskScheduler est probablement meilleure. Bien que cela ait aussi ses mérites:

 // On a new thread try { InitializeProlog(); try { myTs.RunOnCurrentThread(); } finally { ReleaseProlog(); } } catch (Exception ex) { // The global handler }