2015-11-28 92 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 

我仍然不知道被誤導的問題的糾正和完成應被張貼爲答案,但我希望它會對社區做的更好,而不僅僅是關閉它。

相關問題