Building TreeView by Reactive Extensions NET (Recursive observe directory, Iterator function with Yield, Windows native thread).
На цієї сторінці буде описаний невеличкий приклад практичного застосування Reactive Extensions to .Net, для початку лінки на документацію на нього:
- http://introtorx.com/
- http://rxwiki.wikidot.com/101samples
- https://rx.codeplex.com/
- https://www.amazon.com/Introduction-Rx-Lee-Campbell-ebook/dp/B008GM3YPM
Це немаленька бібліотека, яка компілюється у специфічну StateMachine, яка виконує операції MoveNext у багатьох потоках одночасно. На цьому коді нище працює чотири потока .NET, у кожному з яких виконується YIELD.
Ця прога просто обходить файлову структуру, як будь-яка прога у моему блозі, наприклад ось ця - WinDump - снимок состояния системы с помощью WMI, але вона робіть це мультипоточно. І заповнює результатом обхода файлової системи Win-контрол TreeView.
Сподіваюсь, смисл цього коду зрозумілий. Дві змінні, які задані у стрічках 21 та 26, мають евенти, які додають до них бібліотека System.Reactive.DLL. На ці євенти підписуються у стрічках 73 та 84 хандлер outputDirectories або outputDirectory. Коли з'являється перший підписник, тоді сам RX викликає хєндлер GetAllDirectories, заданий у стрічках 137 і далі, який працює у мультипоточному режимі.
1: Imports System.Reactive.Concurrency
2: Imports System.Reactive.Linq
3: Imports System.Threading
4: Imports System.Collections.Generic
5: Imports System.ComponentModel
6: Imports System.Data
7: Imports System.Drawing
8: Imports System.Linq
9: Imports System.Text
10: Imports System.Windows.Forms
11: Imports System.IO
12:
13: ''' <summary>
14: ''' Provides an example of using the RxFramework to get directory listings using a background thread.
15: ''' </summary>
16: Partial Public Class DirectoriesForm
17:
18: ''' <summary>
19: ''' An observable providing a directory as soon as it is encountered.
20: ''' </summary>
21: Private directories As IObservable(Of String)
22:
23: ''' <summary>
24: ''' An observable providing directories in groups.
25: ''' </summary>
26: Private bufferedDirectories As IObservable(Of IList(Of String))
27:
28: ''' <summary>
29: ''' The current observer that is observing on one of the observables.
30: ''' </summary>
31: Private observer As IDisposable
32:
33: Public Sub New()
34: InitializeComponent()
35:
36: Dim syncContext = SynchronizationContext.Current
37:
38: ' Observe the enumeration of all directories using the winforms thread.
39: Me.directories = GetAllDirectories("c:\").
40: ToObservable(Scheduler.ThreadPool).
41: ObserveOn(syncContext)
42:
43: ' Observe the enumeratino of all directories, but buffered in groups
44: ' of 1000 entries each second, and then observe that group on the winforms thread.
45: Me.bufferedDirectories = Observable.
46: Interval(TimeSpan.FromSeconds(1), Scheduler.ThreadPool).
47: Zip(
48: GetAllDirectories("c:\").
49: ToObservable(Scheduler.ThreadPool).
50: Buffer(1000),
51: Function(a, b) b
52: ).
53: ObserveOn(syncContext)
54:
55: Me.butStop.Enabled = False
56: End Sub
57:
58: Private Sub butStop_Click(sender As Object, e As EventArgs) Handles butStop.Click
59: ' Clear out the running observer
60: If Me.observer IsNot Nothing Then
61: Me.observer.Dispose()
62: Me.observer = Nothing
63: Me.butStop.Enabled = False
64: Me.butObserverSingle.Enabled = True
65: Me.butObserveBuffered.Enabled = True
66: End If
67: End Sub
68:
69: Private Sub butObserverSingle_Click(sender As Object, e As EventArgs) Handles butObserverSingle.Click
70: Me.treeViewDirectories.Nodes.Clear()
71: If Me.observer Is Nothing Then
72: ' Observe on the single directories.
73: Me.observer = Me.directories.Subscribe(AddressOf outputDirectory)
74: Me.butStop.Enabled = True
75: Me.butObserverSingle.Enabled = False
76: Me.butObserveBuffered.Enabled = False
77: End If
78: End Sub
79:
80: Private Sub butObserveBuffered_Click(sender As Object, e As EventArgs) Handles butObserveBuffered.Click
81: Me.treeViewDirectories.Nodes.Clear()
82: If Me.observer Is Nothing Then
83: ' observe on the buffered directories.
84: Me.observer = Me.bufferedDirectories.Subscribe(AddressOf outputDirectories)
85: Me.butStop.Enabled = True
86: Me.butObserverSingle.Enabled = False
87: Me.butObserveBuffered.Enabled = False
88: End If
89: End Sub
90:
91: Private Sub outputDirectory(path As String)
92: ' We check to see if the handle is created because when
93: ' the form is disposing this may still be trying to observe.
94: If Me.treeViewDirectories.IsHandleCreated Then
95: Me.treeViewDirectories.Nodes.Add(path)
96: End If
97: End Sub
98:
99: Private treeNodes As New Dictionary(Of String, TreeNode)()
100:
101: Private Sub outputDirectories(paths As IEnumerable(Of String))
102: ' We check to see if the handle is created because when
103: ' the form is disposing this may still be trying to observe.
104: If Me.treeViewDirectories.IsHandleCreated Then
105: Try
106: Me.treeViewDirectories.BeginUpdate()
107: For Each path__1 In paths
108: Dim sb = New StringBuilder()
109: Dim pieces = path__1.Split(Path.DirectorySeparatorChar)
110: Dim parent As TreeNode = Nothing
111: For i As Integer = 0 To pieces.Length - 1
112: Dim child As TreeNode = Nothing
113: sb.Append(pieces(i))
114: sb.Append(Path.DirectorySeparatorChar)
115:
116: If Not treeNodes.TryGetValue(sb.ToString(), child) Then
117: If parent IsNot Nothing Then
118: child = parent.Nodes.Add(pieces(i))
119: Else
120: child = Me.treeViewDirectories.Nodes.Add(pieces(i))
121: End If
122: treeNodes(sb.ToString()) = child
123: End If
124: parent = child
125: Next
126: Next
127: Finally
128: Me.treeViewDirectories.EndUpdate()
129: End Try
130: End If
131: End Sub
132: ''' <summary>
133: ''' Gets the enumeratino of all directories and sub directories under the given path.
134: ''' </summary>
135: ''' <param name="path">The path to search</param>
136: ''' <returns>The enumeration of all directories and sub directories.</returns>
137: Private Shared Iterator Function GetAllDirectories(path As String) As IEnumerable(Of String)
138: Dim subdirs As String() = Nothing
139: ' Some directories may be inaccessable.
140: Try
141: subdirs = Directory.GetDirectories(path)
142: Catch generatedExceptionName As IOException
143: Catch generatedExceptionName As UnauthorizedAccessException
144: End Try
145: If subdirs IsNot Nothing Then
146: For Each subdir In subdirs
147: Debug.Print(subdir.ToString)
148: Yield subdir
149: For Each grandchild In GetAllDirectories(subdir)
150: Debug.Print(grandchild.ToString)
151: Yield grandchild
152: Next
153: Next
154: End If
155: End Function
156:
157: End Class
Modern version of this library:
|