I just started to learn Async/Await, and I've got a problem. There is a WinForms App with 2 buttons and permanently running status-bar on it. When pressing first button - everithing is fine - cicle running in another thread and UI doesn't freeze. When pressing second - cicle running in Main Thread, so UI freeze. I just can't get it! Why? For me this methods look almost the same. Btw sry my bad english.
Public Class Form1
'Async - Create Task from Sync Method
Private Async Sub btn_async_from_sync_Click(sender As Object, e As EventArgs) Handles btn_async_from_sync.Click
Dim tsk As New Task(Of Integer)(AddressOf func_for_task)
tsk.Start()
Dim val As Integer = Await tsk
MsgBox(val)
End Sub
Private Function func_for_task() As Integer
Dim val As Integer
For i = 1 To 999999999
val += 1
Next
Return val
End Function
'Async - Use existing Task - Async Method
Private Async Sub btn_async_use_task_Click(sender As Object, e As EventArgs) Handles btn_async_use_task.Click
Dim tsk As Task(Of Integer) = func_for_task_async()
Dim val As Integer = Await tsk
MsgBox(val)
End Sub
Private Async Function func_for_task_async() As Task(Of Integer)
Dim val As Integer
For i = 1 To 999999999
val += 1
Next
Return val
End Function
End Class
Don't ignore compiler warnings. In this case, the compiler will warn you that your async method does not use await, and therefore will run synchronously.
In your case, you can use Task.Run to execute func_for_task on a background thread, and await it from your click event handler.
For more info, see the async/await intro on my blog.
Related
Firstly, new thread born independently of my code by external factory.
I have something integer key variable - "CODE". This variable "CODE" I received as result of a lot of calculating and request to DB (and maybe I need multi-threading protection of this "CODE").
I need lock thread only for the same "CODE", if currently "CODE" is handling now, thread with other "CODE" can not locking and handling without obstacle.
Thread with "CODE" now handling and locking need to immediately finish.
Also all this function must be working asynchronously with ASYNC/AWAIT.
I am using .NET Core 6.
It looks like a very common task, but what mechanism do I need to use?
Does code template for this task exist in inet?
This is my solution, but I'm not sure how it working with high uploading. I only try to test this code with high concurrency. If anybody has advice for me, please.
I realize solution with ConcurrentDictionary and locking critical section. To unlock main locker and using secondary locker I created new task.
This is schema of my solution.
Public Sub New(ByVal logger As ILogger(...)
....
WorkingServer = New ConcurrentDictionary(Of Integer, BashJob)()
WorkingVm = New ConcurrentDictionary(Of Integer, BashJob)()
End Sub
Private WorkingServer As ConcurrentDictionary(Of Integer, BashJob)
Private RequestNextJob As New Object
Public Async Function Execute(context As IJobExecutionContext) As Task Implements IJob.Execute
_logger.LogInformation($"Time {Now}")
Interlocked.Increment(Counter)
SyncLock RequestNextJob
' calculate CODE hidden in NextJob
...
Dim NextJob As BashJob = Res1.Result.Item1(0)
Dim Val1 As BashJob
Dim ServerWorking As Boolean = WorkingServer.TryGetValue(NextJob.toServer, Val1)
If Not ServerWorking Then
Dim AddSucess1 = WorkingServer.TryAdd(NextJob.toServer, NextJob)
If AddSucess1 Then
Dim ServerThread = New Thread(Sub() ServerJob(NextJob, New ServerClosure))
ServerThread.Start()
Else
Exit Function
End If
Else
Exit Function
End If
End SyncLock
End Function
Async Sub ServerJob(ByVal Prm As BashJob, ByVal Closure As ServerClosure)
Try
Closure.Res = Sql.ExecNonQuery(...)
...
'main processor
...
WorkingServer.TryRemove(Prm.toServer, Prm)
Catch ex As Exception
_logger.LogInformation($"ServerSsh ({Counter.ToString}) {Now.ToString} Server:{Prm.toServer} {Prm.i}:[{Prm.Command}] GettingError {ex.Message}")
Finally
WorkingServer.TryRemove(Prm.toServer, Prm)
End Try
End Sub
End Class
Public Class ServerClosure
Property Res As Integer
Property Server As ServerBashAsync
Property Connect As Tuple(Of Renci.SshNet.SshClient, Exception, Exception)
Property BashRet As Task(Of String)
Property IsCompleted As Integer
Property IsCompletedWithErr As Integer
End Class
I need expand this code and will be call this code from Quartz thread and than notify frontend by SignalR, because operation will be going by hours.
But as first step I call this code directly from ASP.NET Core thread
Public Function AsyncBash(Model As KvmBashRequest) As IActionResult
Return Ok(Server.BashAsync(Model.BashCmd))
....
Public Async Function BashAsync(BashCmd As String) As Task(Of String)
Dim CTX = New Threading.CancellationToken()
If SshClient.IsConnected Then
Try
Dim Cmd1 = SshClient.CreateCommand(BashCmd)
Await Cmd1.ExecuteAsync(New Progress(Of SshOutputLine), CTX)
Catch ex As Exception
Return ex.Message
End Try
Else
Return "not connected"
End If
End Function
.....
Public Class SshOutputLine
Public Sub New(ByVal Line As String, ByVal IsErrorLine As Boolean)
Debug.WriteLine($"{IsErrorLine}:{Line}")
End Sub
End Class
.....
Imports System.IO
Imports System.Runtime.CompilerServices
Imports System.Threading
Imports Renci.SshNet
Module SshCommandExtensions
<Extension()>
Async Function ExecuteAsync(ByVal SshCommand As SshCommand, ByVal OutputLine As IProgress(Of SshOutputLine), ByVal CTX As CancellationToken) As Task
Dim AsyncResult = SshCommand.BeginExecute()
Dim StdoutSR = New StreamReader(SshCommand.OutputStream)
Dim StderrSR = New StreamReader(SshCommand.ExtendedOutputStream)
While Not AsyncResult.IsCompleted
Await Progress(SshCommand, StdoutSR, StderrSR, OutputLine, CTX)
Thread.Yield()
End While
SshCommand.EndExecute(AsyncResult)
Await Progress(SshCommand, StdoutSR, StderrSR, OutputLine, CTX)
End Function
Private Async Function Progress(ByVal SshCommand As SshCommand, ByVal StdoutSR As TextReader, ByVal StderrSR As TextReader, ByVal OutputLine As IProgress(Of SshOutputLine), ByVal CTX As CancellationToken) As Task
If CTX.IsCancellationRequested Then SshCommand.CancelAsync()
CTX.ThrowIfCancellationRequested()
Await OutProgress(StdoutSR, OutputLine)
Await ErrProgress(StderrSR, OutputLine)
End Function
Private Async Function OutProgress(ByVal StdoutSR As TextReader, ByVal StdoutProgress As IProgress(Of SshOutputLine)) As Task
Dim StdoutLine = Await StdoutSR.ReadToEndAsync()
If Not String.IsNullOrEmpty(StdoutLine) Then StdoutProgress.Report(New SshOutputLine(Line:=StdoutLine, IsErrorLine:=False))
End Function
Private Async Function ErrProgress(ByVal StderrSR As TextReader, ByVal stderrProgress As IProgress(Of SshOutputLine)) As Task
Dim StderrLine = Await StderrSR.ReadToEndAsync()
If Not String.IsNullOrEmpty(StderrLine) Then stderrProgress.Report(New SshOutputLine(Line:=StderrLine, IsErrorLine:=True))
End Function
End Module
Unfortunately, this code is working fully synchronously, I expect a serious of call my class SshOutputLine, but I get it only once.
SSH result
This is thread tree of my Web API:
Threading
What is wrong in my code?
After thinking I have understanding issue, maybe call Async function is wrong. Correct way with Task.Run (wrong line commented)
Public Overloads Async Function Bash(BashCmd As String) As Task(Of String)
Dim CTX = New Threading.CancellationTokenSource()
If SshClient.IsConnected Then
Try
Dim Cmd1 = SshClient.CreateCommand(BashCmd)
Await Task.Run(Function() Cmd1.ExecuteAsync(New Progress(Of SshOutputLine), CTX.Token, SshOutput, SshErrMsg))
'Await Cmd1.ExecuteAsync(New Progress(Of SshOutputLine), CTX)
Catch ex As Exception
Return ex.Message
End Try
Else
Await Task.Run(Function() "not connected")
End If
End Function
Second issue - need to really huge output like "ls -R -la /" to test this future. For huge Linux server with 21 millions of output chars buffer was fill and flush only for times.
Huge output
Buffer size
NewLine is ready exactly in Thread.Yield.
Thread.Yield
I can not publish full code, sorry, because my code little bit hard to understanding for C# programmers, because I use Shadow and other strong OOP concepts beyond understanding of C# programmers.
OOP wrapper
Hello I am running a VBA script in excel 365, and am trying to make an object (day) store a list of custom objects (job) but when trying to input the array of objects I run into an error.
I am using Property handlers to access the private internal array for the day object.
This is how it gets ran, the jobTest function only creates some arbitrary job objects.
Sub dayTest()
jobTest
Debug.Print testJob.GNum
Dim foo As New day
foo.DateBlock = DateValue("14 / 03 / 2020")
Debug.Print TypeName(testJob)
Dim x As Variant
x = foo.JobsForDay(0, testJob)
End Sub
When running this code I get the error:
Wrong number of arguments or property assignment
But when looking at my access methods I created, these don't seem to apply.
Private mDateStored As Date
Private mNumJobs As Long
Private mJobsForDay() As job
Property Get JobsForDay(val As Long) As Variant
JobsForDay = mJobsForDay(val)
End Property
Property Let JobsForDay(val As Long, jobObj As Variant)
mNumJobs = mNumJobs + 1
ReDim Preserve mJobsForDay(mNumJobs)
mJobsForDay(val) = jobObj
End Property
I am trying to call the Let function, however the compiler throws a generic "Expected =" error when I run the code as
foo.JobsForDay(0, testJob)
Which is why I have the variant x accepting all input from the method.
Thanks for any help!
In the code below, StartSocketListener has been started as a Thread. Sometimes, the processing of a message takes long enough that a couple of messages stack up (or at least that appears to be the case). I thought I'd take the socket and start another thread with it. I thought that passing the _HostConnection into the task would make concurrent instances of MessageFunction thread safe. Not so, it appears. I regularly get two kinds of errors: one is at _HostReader.ReadString, where I either read past the end of the stream or read only a portion of the stream (as if something else has consumed part of it). The other is at _HostWriter.Write, where I get "an established connection was aborted." Is it obvious where I've failed to get the thread safety that I was expecting? [I am a C# developer, so the VB lambda may look awkward :) ]
Private Sub StartSocketListener()
Dim _Listener As TcpListener = Nothing
Dim _HostConnection As Socket
Try
_Listener = New TcpListener(IPAddress.Parse(GetIPAddress), _TCPListenerPort)
_Listener.Start()
While _KeepListening
_HostConnection = _Listener.AcceptSocket
Dim MessageFunction = Sub(socket As Socket)
Dim _ClientIP As String = socket.RemoteEndPoint.ToString
Dim _HostSocketStream As NetworkStream = New NetworkStream(socket)
Dim _HostWriter As BinaryWriter = New BinaryWriter(_HostSocketStream)
Dim _HostReader As BinaryReader = New BinaryReader(_HostSocketStream)
Try
Dim _MsgXML As String = _HostReader.ReadString
If _MsgXML.Trim <> "" Then
If _KeepListening Then
Dim _RetXML As String = ProcessMessage(_MsgXML, _ClientIP)
Try
_HostWriter.Write(_RetXML)
Catch ex As Exception
...
End Try
End If
End If
Catch ex As Exception
...
End Try
_HostReader.Close()
_HostWriter.Close()
_HostSocketStream.Close()
socket.Close()
End Sub
Task.Factory.StartNew(Sub() MessageFunction(_HostConnection))
End While
_Listener.Stop()
Catch ex As Exception
....
End Try
End Sub
I have a working 3D object viewer in VB.net (I know that VB.net is not the best language to use for this but still)
So if I press the W key the box moves up. If I press the D key it moves to the right. But I wanna do the simultaneously. And to do so I figured that I could give each key its own thread.
So the is the code I wound upwith.
Dim thread1 As System.Threading.Thread
Dim thread2 As System.Threading.Thread
Private Sub MoveUp_Keydown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles GlControl1.KeyDown
If e.KeyCode = Keys.W Then
If NumericUpDown1.Value < 100 Then
NumericUpDown1.Value = NumericUpDown1.Value + 1
Me.Refresh()
End If
End If
End Sub
Private Sub MoveUp1_Keydown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles GlControl1.KeyDown
If e.KeyCode = Keys.W Then
thread1 = New System.Threading.Thread(AddressOf MoveUp_Keydown)
End If
End Sub
But the error I am getting is
error BC30518: Overload resolution failed because no accessible 'New' can be called with these arguments
I have tried to google this but the problem is that nobody uses the threading for a keypress resulting in different solutions.
Thanks for any help
You can do it without a Timer, but still using the GetKeyState() API:
Public Class Form1
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True, CharSet:=Runtime.InteropServices.CharSet.Unicode)> _
Private Shared Function GetKeyState(ByVal nVirtKey As Integer) As Short
End Function
Private Sub Form1_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
If IsKeyDown(Keys.W) Then
If NumericUpDown1.Value < 100 Then
NumericUpDown1.Value = NumericUpDown1.Value + 1
Me.Refresh()
End If
End If
If IsKeyDown(Keys.D) Then
If NumericUpDown2.Value < 100 Then
NumericUpDown2.Value = NumericUpDown2.Value + 1
Me.Refresh()
End If
End If
' ...etc...
End Sub
Private Function IsKeyDown(ByVal key As Keys) As Boolean
Return GetKeyState(key) < 0
End Function
End Class
The KeyDown() event will fire when any key is being held down, and you simply check each key you're interested in with GetKeyState(). There is no need for multiple threads...and I'm not even sure how that would work with form events. I don't know what you wanted to do with the "D" key, so I just put in NumericUpDown2 so you could see each block can do a different thing. You might not want to call Me.Refresh until the very bottom of the KeyDown() event so it only gets called once.