在特定线程上运行工作 [英] Run work on specific thread

查看:91
本文介绍了在特定线程上运行工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想拥有一个特定的线程,将任务排队并在该单独的线程中处理任务.该应用程序将根据用户使用情况创建任务,并将其排队到任务队列中.然后,单独的线程处理任务.即使线程为空,保持线程活动并使用它来处理排队的任务也很重要.

I would like to have one specific thread, queue for Tasks and process tasks in that separate thread. The application would make Tasks based on users usage and queue them into task queue. Then the separate thread processes the tasks. It is vital to keep the thread alive and use it for processing queued tasks even if queue is empty.

我已经尝试过使用BlockingCollectionTaskScheduler的几种实现,并将并发限制为仅一个线程,但是当队列为空并且Task由其他线程处理时,似乎会丢弃该线程.

I have tried several implementations of TaskScheduler with BlockingCollection and limit the concurrency to only one thread but it seems the Thread gets disposed when queue gets empty and the Task is processed by other thread.

您能否至少请我参考一些资源来实现这一目标?

Can you please at least refer me to some sources how to achieve this goal?

tl;博士 试图限制一个特定的线程来处理动态添加到队列中的任务.

tl;dr Trying to limit one specific thread to process tasks which are dynamically added to the queue.

Edit1:

这是使用WCF和.NET Framework 4.6的实验性Web应用程序.在WCF库中,我试图通过一个线程处理任务来实现此行为.这个线程必须使用外部dll库初始化prolog,然后才能使用prolog.如果在进程中使用其他线程,则库抛出AccessViolationException.我已经进行了一些研究,这很可能是由于该库中的线程管理不善所致.我有实现,我到处都有锁,并且它起作用了.我现在正在尝试重新实现并使其异步,因此我不会通过锁定来阻塞主线程.

This is experimental web app that uses WCF and .NET framework 4.6. In the WCF library, I am trying to implement this behaviour with one thread processing tasks. This one thread must init prolog using external dll library and then do work with prolog. If other thread is used in process, library throws AccessViolationException. I've done some research and this is most probably because of badly managed threading in that library. I had implementation where I had locks everywhere and it worked. I am now trying to reimplement and make it asynchronous so I don't block the main thread with locking.

我不在电脑旁,但今天晚些时候回家时,我提供了一些代码.

I am not at my computer but I provide some code when I get home later today.

推荐答案

您的方法看起来不错,所以您可能只是犯了一些小小的愚蠢错误.

Your approach seems fine, so you probably just made some tiny stupid mistake.

进行简单的自定义TaskScheduler实际上很容易.对于您的情况:

It's actually pretty easy to make a simple custom TaskScheduler. For your case:

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(string 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<Task> taskQueue;

  public SingleThreadTaskScheduler(CancellationToken cancellationToken)
  {
      this.cancellationToken = cancellationToken;
      this.taskQueue = new BlockingCollection<Task>();
  }

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

修改它非常容易,以使您可以完全控制任务计划程序实际执行任务的位置-实际上,我已经从我以前使用的仅具有RunOnCurrentThread方法的任务计划程序中改编了此方法.公开的.

It's pretty easy to modify this to give you full control on where the task scheduler is actually executing the task - in fact, I've adapted this from a previous task scheduler I've used which simply had the RunOnCurrentThread method public.

对于您的情况,您总是想只坚持一个线程,因此SingleThreadTaskScheduler中的方法可能更好.尽管这也有其优点:

For your case, where you always want to stick to just the one thread, the approach in SingleThreadTaskScheduler is probably better. Although this also has its merits:

// On a new thread
try
{
  InitializeProlog();

  try
  {
    myTs.RunOnCurrentThread();
  }
  finally
  {
    ReleaseProlog();
  }
}
catch (Exception ex)
{
  // The global handler
}

这篇关于在特定线程上运行工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆