我试图实现以下目标:
主线程应该创建一些任务(实际上,可能有很多线程,但是现在让我们简化一下以避免额外的复杂性和并发性),这些以某种方式安排并在以后执行。可能是某些Timer
以固定的时间间隔,也许稍后在同一个线程上阻塞这些任务时,可能是由另一个专门为此任务指定的线程 - 现在没有很好地定义调度的实现,我只想了解基本想法。
我希望我的代码看起来像这样:
'Somewhere in the main thread...
Dim MyTask = CreateTask(Of Integer)(Function()
Console.WriteLine("Task has been called!")
'Some activity...
Return 42
End Sub)
'...
UseTheResult(MyTask.Result)
...其中CreateTask
是响应将委托作为后续执行任务进行调度的子程序。
问题是,我不知道如何实现这个概念。起初,我尝试使用SynchronizationContext
,但它似乎更多的是私有线程内存,与调度任务无关(尽管Windows UI似乎以某种方式使用它来实现此目标 - 我还不清楚所有这些情况。然后我偶然发现TaskScheduler
TaskFactory
,与TaskScheduler
一起,似乎只做了这件事。我试图让我实现这个帖子,但是我的代码没有按预期工作,这意味着我遗漏了一些非常重要的关于TaskScheduler
如何工作的事情,甚至更糟 - 关于这整个概念。我已经阅读了一些关于这个主题的文章(最值得注意的是Public Sub Main()
Dim T As New TaskFactory(New TestScheduler)
Dim tasks As New List(Of Task)
For I = 1 To 100
Dim J = I
tasks.Add(T.StartNew(Sub() Console.WriteLine(J)))
Next I
Task.WaitAll(tasks.ToArray)
Console.WriteLine("Test end")
Console.ReadLine()
End Sub
Class TestScheduler : Inherits TaskScheduler
Dim Tasks As New Collections.Concurrent.ConcurrentQueue(Of Task)
'Dim Counter As New Threading.AutoResetEvent(False)
Dim CTimer As New Threading.Timer(AddressOf TimerUp, Nothing, 0, 100)
Protected Overrides Function GetScheduledTasks() As IEnumerable(Of Task)
Return Tasks
End Function
Private Sub TimerUp(State As Object)
Static AllTasks% = 0
Dim T As task = Nothing
If Tasks.TryDequeue(T) Then
Console.WriteLine("Task... {0}", AllTasks) 'for debugging
AllTasks += 1
If Not MyBase.TryExecuteTask(T) Then
QueueTask(T)
End If
End If
End Sub
Protected Overrides Sub QueueTask(task As Task)
Tasks.Enqueue(task)
End Sub
Protected Overrides Function TryExecuteTaskInline(task As Task, taskWasPreviouslyQueued As Boolean) As Boolean
If taskWasPreviouslyQueued Then Return False
Return MyBase.TryExecuteTask(task)
End Function
End Class
'Output:
'2
'1
'2
'... and that's it, no even "Test end" or any other output whatsoever.
'I have strong feeling that it throws an exception somewhere; however,
'when I try to debug it, it silently stops, without waiting for <Enter>,
'and when I run it without debugging - it produces the abovementioned output without
'any exceptions or messages.
SynchronizationContext
及其中的代码示例),但仍然不了解这是如何工作的。
我的工作符合预期的代码,它应该安排然后执行任务,延迟时间为100毫秒(所以整个测试应该运行大约10秒):
TaskScheduler
我非常强烈地感觉到我在这里误解了一些基本概念。
总而言之,我的问题是:如何在.NET中自定义任务计划?
我应该使用什么 - QueueTask
,TryExecuteTaskInline
,派生类或其他什么?如果是这样,我应该覆盖什么是确切的方法,它们的含义是什么?所有这些TryExecuteTask
,TaskScheduler.TryExecuteTask
和False
只会让我发疯...
P.S。:请原谅,如果您愿意,请更正我在那里所犯的任何语法错误或逻辑错误。英语不是我的母语,而且我现在还没有非常清楚地思考[睡眠不足]。谢谢!
编辑:我很抱歉误导你们,每个人。代码工作非常好,我忘记将我的项目标记为&#34; Start-Up&#34; - 所以不小心在同一个解决方案中运行另一个测试项目......起初我打算关闭这个问题,但后来我意识到最好回答它 - 它应该可以帮助任何有同样问题的人,而且,大多数重要的是,允许一个更好的解决方案:到目前为止,许多问题没有得到答复。为什么我们需要TryExecuteTaskInline
- 为什么&#34;尝试&#34;,确切地说?该方法何时可以返回taskWasPreviouslyQueued = true
? {{1}}方法何时运行,何时可以使用{{1}}执行?我有一些假设,但同样,他们可能是错的。
我是否需要将这些主题作为单独的问题提出,或者,由于它们仍然属于这个问题的标题,他们可以在那里吗?
答案 0 :(得分:0)
因此,为了实现这一目标,可以使用TaskScheduler
和TaskFactory
的组合,这对我来说非常有用:
Public Sub Main()
Dim T As New TaskFactory(New TestScheduler)
Dim tasks As New List(Of Task)
For I = 1 To 100
Dim J = I
tasks.Add(T.StartNew(Sub() Console.WriteLine(J)))
Next I
Task.WaitAll(tasks.ToArray)
Console.WriteLine("Test end")
Console.ReadLine()
End Sub
Public Function CreateTask(ActionToSchedule As Action) As Task
'This is exactly the needed function!
'A single static instances of both TaskFactory and custom TaskSheduler are used.
Static Factory As New TaskFactory(New TestScheduler)
Return Factory.StartNew(ActionToSchedule)
End Function
Class TestScheduler : Inherits TaskScheduler
Dim Tasks As New Collections.Concurrent.ConcurrentQueue(Of Task)
Dim CTimer As New Threading.Timer(AddressOf TimerUp, Nothing, 0, 10)
Protected Overrides Function GetScheduledTasks() As IEnumerable(Of Task)
Return Tasks
End Function
Private Sub TimerUp(State As Object)
Static AllTasks% = 0
Dim T As task = Nothing
If Tasks.TryDequeue(T) Then
Console.WriteLine("Task... {0}", AllTasks) 'for debugging purposes
AllTasks += 1
If Not MyBase.TryExecuteTask(T) Then
QueueTask(T)
End If
End If
End Sub
Protected Overrides Sub QueueTask(task As Task)
'Here you should schedule the given task for later execution, whatever that means.
'In this case it is simply put in the queue to be executed later when the timer fires.
Tasks.Enqueue(task)
End Sub
Protected Overrides Function TryExecuteTaskInline(task As Task, taskWasPreviouslyQueued As Boolean) As Boolean
'> Attempts to execute the specified task on the current thread.
'As far as I understand - when the thread starts to wait for the task completion, either by trying to read
'"task.Result" or executing "T.RunSynchronously()", this function will allow to execute the task synchronously
'on the waiting thread instead of blocking, which may lead to a huge performance&speed gain.
'If our task is not executing - waiting somewhere in the queue or is not even enqueued yet - we
'may dequeue it and run instantly to avoid the blocking.
Return False
'In this particular case, I've decided that the timer should run all the tasks, and, therefore, there must be done
'no inlining. Hovewer, the code below (obviously, it will not execute) should illustrate
'this idea.
If taskWasPreviouslyQueued Then
'If the task is already enqueued, then we may try to remove it from
'the queue and execute there instead.
If Tasks.TryDequeue(task) Then
Return MyBase.TryExecuteTask(task)
Else
'It has already been dequeued and maybe even completed already, so - no, no inlining. :)
Return False
End If
Else
'If it wasn't even enqueued - ha! No questions - we may run it now without any preparations.
Return MyBase.TryExecuteTask(task)
End If
End Function
End Class
我仍然不确定错误引导的问题的纠正和完成是否应该作为答案发布,但我希望它能为社区带来更多好处,而不仅仅是关闭它。