Righteous Wrath Online Community

General => Tech Chat => Topic started by: Darren Dirt on June 01, 2020, 09:44:22 AM

Title: CSharp C# .Net questions
Post by: Darren Dirt on June 01, 2020, 09:44:22 AM
I figure it can be helpful to share my questions as I proceed with digging deep into stuff.

And I do mean deep** ... because I understand there are COUNTLESS tutorials for nailing down the BASIC fundamental concepts etc... and as I trudge through that stuff I get distracted by side quests, then I try building a simple thingie that does something beyond the simple, and I definitely like to "figure things out" on my own as that is half the fun of any coding task, no interest in just being told an answer to a question.

On my own I have found amazingly helpful sites including https://sharplab.io/ (where I can glance at the IL to compare 2 different algorithms to see what they actually do, for example) and as I began building a basic game engine of some kind (because that's fun to me! Not playing games or even building games, lol) I of course stumbled upon the usually-extremely-helpful http://pinvoke.net/

But sometimes I will share something here that is "I am trying to accomplish X, I tried Y, but problem Z is occurring -- any suggestions you .Net veterans?"




** on the topic of "deep", heck I even spent my own cash to buy this highly-recommended book from a very prolific and highly respected SO'er:
https://www.manning.com/books/c-sharp-in-depth-fourth-edition

Title: Re: CSharp C# .Net questions
Post by: Darren Dirt on June 01, 2020, 10:30:45 AM
Sometimes I want something similar to DOS [TIMEOUT delayInSeconds] https://ss64.com/nt/timeout.html

Over the last few months as needs expanded I built this "full featured function", which also allows for returning the key pressed in case I care.
static ConsoleKeyInfo Pause(double msDelay) {
DateTime dtFinished = DateTime.Now.AddMilliseconds(msDelay);
while(true) {
if(Console.KeyAvailable)return Console.ReadKey(true);
if(DateTime.Now >= dtFinished)break;
System.Threading.Thread.Sleep(1);
}
return new ConsoleKeyInfo();
}
But often I don't care about which key is pressed, I just want an easy copypasta to say "Here is a message" and wait for any key press OR a timeout, then proceed with the next piece of code execution.

So as I did some learning about Threads and Tasks, and leaning towards not relying on the higher-overhead way of using "aync" on the method and "await" in the function code itself, I came up with this simplified single-liner code, that I was happy to find SEEMS to do exactly what I wanted in an easy-to-paste way:
{var t = System.Threading.Tasks.Task<ConsoleKeyInfo>.Run( () => Console.ReadKey(true) ); t.Wait(DELAY_IN_MS);}

However, it only seemed that way.

Let's say I have a dozen of these calls with a delay for each to allow me to look at the output etc.
So I let 8 of them time out, but then on #9 I actually press a key to quickly skip past the delay.
Guess what happens? Instead of instantly recognizing the keypress, nope, now I have to press a key EIGHT TIMES before #9 recognizes the key press!

So it seems the hook into the getting of Console.ReadKey is never freed up until a keypress actually happens; the blocking call seems to be "held on" even after the "t" variable has vanished out of scope(!)

Probably because it was launched inside a Task, and unlike a Thread I can't just insta-kill it, I would have to pass it a cancellation token, and do a check afterwards if its status was completed successfully or aborted/failed. :-\

I even looked through the DOTNET code for "console.cs" to see exactly how it does its thing...
https://referencesource.microsoft.com/#mscorlib/system/console.cs,1476

...and so I wonder how I can tell the Win32Native "ReadConsoleInput" call to "never mind, go away, let go of everything, thanks!"

Because I suspect I will not be able to come up with a simple single-line version without heading towards the complexity of my more full featured function :-\
I mean, sure I can just include that function and then call it as needed (as I had been doing) but I was really hoping I could come up with a thread/task based simplified version.

If it's not really easily accomplished (without some P/Invoke fun) then I might have to settle for the reduced-functionality version of my full featured Pause:
public static void Timeout(double msDelay = 90000, bool shouldIgnoreKeyStrokes = false) {
DateTime dtFinished = DateTime.Now.AddMilliseconds(msDelay);
while(DateTime.Now < dtFinished) {
if(!shouldIgnoreKeyStrokes && Console.KeyAvailable) {
Console.ReadKey(true); return;
}
System.Threading.Thread.Sleep(1);
}
}
Title: Re: CSharp C# .Net questions
Post by: Darren Dirt on June 02, 2020, 08:47:45 PM
Bumped because surprisingly no responses...
Title: Re: CSharp C# .Net questions
Post by: Mr. Analog on June 02, 2020, 09:33:09 PM
I haven't done much console dev and I'm a bit rusty at writing my own multithreaded code so I haven't really given it much thought

What is it you are trying to do?

Sent from my Pixel 2 XL using Tapatalk

Title: Re: CSharp C# .Net questions
Post by: Darren Dirt on June 11, 2020, 03:28:06 PM
As usual I am being a hyper-completionist, I think.

I think I may have just made the mistake of applying a threading/task solution to the wrong problem, especially since the way the Console.ReadKey actually works under the hood seems to imply it will hook/block even after the timeout happens, so it's not a good idea to do that. In the end I am just gonna keep [re-]using the updated function I put at the end of the post above ("the reduced-functionality version of my full featured Pause") ... [and btw the "System.Threading.Thread.Sleep(1);" part is to prevent it from going to 40%-100% CPU usage lol].


AKA this is Good Enough, I don't know why I thought i need something more complicated than a function call lol...

// some little widget or code snippet thingie that I am tinkering with of which the output I might need 1 second or 60 seconds to analyze
Console.Write("Press any key or wait 60 seconds:"); Timeout(60000);
// etc.

Plus now it's got the same [arg1, arg2] order and purpose as the DOS function ;-)


Title: Re: CSharp C# .Net questions
Post by: Mr. Analog on June 11, 2020, 04:17:06 PM
Right on, simple is usually better anyway

Sent from my SM-T810 using Tapatalk

Title: Re: CSharp C# .Net questions
Post by: Darren Dirt on June 12, 2020, 04:26:52 PM
Okay new question... attached is .CS file (named "DynamicFunctionTest.cs.txt") that can be compiled as-is (no dependencies) to demonstrate benchmarking for each option (ignore the warnings about b1..b6 being never used, when you compile it with first line commented out to make the benchmark timing tests run).


Let's say I had some function "bar" that did some logic then as a final step it will call foo(args) -- but "foo" might be any one of a variety of functions foo1 or foo2 or fooN.

Whether foo is foo1 or foo2 or fooN would be determined at runtime.

One option is commenting out all but the "version" I want to use right now...
#define FLAG1
// #define FLAG2
// #define FLAGN
and then inside "bar" after the common logic, a direct call to the appropriate version of foo:
#if FLAG1
  foo1(args);
#elif FLAG2
  foo2(args);
#elif FLAGN
  fooN(args);
#endif

But tbh I was hoping to be able to mimic the simplicity of what can very easily be done in Javascript etc.

So then I had a look at using
Func<args returnvaluetype> (or Action<args> if void return type).
Or with the more verbose syntax via Delegate.

With those I made sure the same logic runs, and did some timing tests -- again making it set the Func/Delegate at runtime based on #define FLAGx -- and WOW I was surprised to see this makes it take TWICE AS LONG as using the direct call.

1. Why is that? I had a look at the IL and JIT Asm via https://sharplab.io/ (make sure you uncomment // #define LOG_INSTEAD_OF_BENCHMARK) and I can see it does extra .Invoke and .BeginInvoke and .EndInvoke so that is probably the reason (relies on Reflection maybe?) -- or is there something else going on that has so much more processing in a simple function call?


2. Any other suggestion for a solution that is closer to a dynamic Javascript callback type of thing?