C# 在特定线程上运行工作
我希望有一个特定的线程,任务队列和进程任务在这个单独的线程中。应用程序将根据用户使用情况生成任务,并将其排入任务队列。然后,单独的线程处理这些任务。即使队列为空,保持线程活动并将其用于处理排队的任务也是至关重要的 我用C# 在特定线程上运行工作,c#,multithreading,task,C#,Multithreading,Task,我希望有一个特定的线程,任务队列和进程任务在这个单独的线程中。应用程序将根据用户使用情况生成任务,并将其排入任务队列。然后,单独的线程处理这些任务。即使队列为空,保持线程活动并将其用于处理排队的任务也是至关重要的 我用BlockingCollection尝试了几个TaskScheduler的实现,并将并发性限制为仅一个线程,但当队列变空且任务由另一个线程处理时,线程似乎会被释放 你能至少向我介绍一些如何实现这一目标的信息来源吗 tl;博士 试图将一个特定线程限制为处理动态添加到队列中的任务 编辑
BlockingCollection
尝试了几个TaskScheduler
的实现,并将并发性限制为仅一个线程,但当队列变空且任务由另一个线程处理时,线程似乎会被释放
你能至少向我介绍一些如何实现这一目标的信息来源吗
tl;博士
试图将一个特定线程限制为处理动态添加到队列中的任务
编辑1:
这是一个实验性的web应用程序,使用WCF和.NET framework 4.6。在WCF库中,我试图用单线程处理任务实现这种行为。这一个线程必须使用外部dll库初始化prolog,然后使用prolog。若进程中使用了其他线程,库将抛出AccessViolationException
。我做了一些研究,这很可能是因为库中的线程管理不好。我有一个到处都有锁的实现,它可以工作。我现在尝试重新实现并使其异步,这样我就不会用锁阻塞主线程
我不在电脑旁,但今天晚些时候回家时,我会提供一些代码。你的方法似乎很好,所以你可能只是犯了一个小小的愚蠢错误 实际上,制作一个简单的自定义任务调度器非常容易。对于您的情况:
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);
}
}
请包含代码示例以突出显示问题。最重要的是,需求的根本原因是什么?我编辑了我的问题,我将在大约10小时后回家时提供代码。你是对的,愚蠢的错误-我有太多线程试图创建新的引擎对象,因为没有锁,所以更多的线程试图一次初始化prolog。谢谢你的日程安排,我有一些类似的东西,但它非常沉重和混乱,你的看起来很好和干净!谢谢!下面是一个工作示例(概念验证)
// On a new thread
try
{
InitializeProlog();
try
{
myTs.RunOnCurrentThread();
}
finally
{
ReleaseProlog();
}
}
catch (Exception ex)
{
// The global handler
}