2015-11-28 30 views
1

我试图实现以下目标:如何在.NET中自定义任务调度?

主线程应该创建一些任务(实际上,可能有很多线程,但现在让我们来简化事情,以避免额外的与并发和并行的复杂性),这是莫名其妙预定并在稍后执行。也许某些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似乎以某种方式用于此目标 - 我还没有清楚地理解所有这些背景thingie)。然后我偶然发现了TaskScheduler,它与TaskFactory相似,似乎只是做这件事。我试图使我的这个线程的实现,但我的代码没有按预期工作,这意味着我错过了一些非常重要的事情,如何TaskScheduler工作,甚至更糟 - 关于这个整体概念。我已经阅读了一些关于这个主题的文章(最显着的是,TaskScheduler上的MSDN documentation及其代码示例),但仍不明白这是如何工作的。

我不-工作,如预期的代码,这本来是安排,然后在它们之间具有100ms的延迟执行任务(所以整个测试应该已经运行了〜10秒):

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. 

我有很强烈的感觉,我误解了这里的一些基本概念。

因此,总结一下,我的问题是:如何自定义.NET中的任务调度

我应该使用什么 - SynchronizationContext,TaskScheduler,派生类或其他东西?如果是这样,我应该覆盖哪些确切的方法,它们的含义是什么?所有这些QueueTaskTryExecuteTaskInlineTryExecuteTask只是缠着我疯了...

P.S:请原谅,如果你愿意的话,纠正任何语法或逻辑错误,我做了那里。英语不是我的本地人,现在我没有想太清楚[睡着了]。谢谢!

编辑:我很抱歉误导你,大家。该代码正在工作完美无缺,我刚刚犯了一个非常粗略的错误,忘记将我的项目标记为“启动” - 并且意外地在同一解决方案中运行另一个测试项目......起初,我要去为了解决这个问题,但后来我意识到最好能够回答它 - 它应该帮助有同样问题的人,最重要的是,允许有更好的解决方案:到目前为止还有很多问题没有得到解答。为什么我们需要TaskScheduler.TryExecuteTask - 为什么“尝试”,到底是什么?什么时候该方法可以返回FalseTryExecuteTaskInline方法何时运行,以及何时可以执行taskWasPreviouslyQueued = true?我有一些假设,但是,他们也可能是错的。

我是否需要将这些主题作为单独的问题提出,或者由于它们仍然属于这个问题的标题,它们可以在那里很好吗?

+0

你使用过'Semaphore'类或SemaphoreSlim吗? –

+0

我有点困惑。 'Semaphore'类应该如何提供帮助?据我所知,它与任务调度无关。 – DeFazer

回答

0

因此,为了实现这一目标,一个可以使用的TaskSchedulerTaskFactory的组合,完美地为我工作:

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 

我仍然不知道被误导的问题的纠正和完成应被张贴为答案,但我希望它会对社区做的更好,而不仅仅是关闭它。

相关问题