How to Async
-
Found in real world production code:
public class ServiceWrapper { //The variable below is hard to read, but if it was camel-case, this would be a bool called //"formHasSoapCompleted1". The name alone should send chills down your spine. Honestly, the //only good thing here is that there is no "formhassoapcompleted2" bool formhassoapcompleted1 = false; //Ah, love that 'l' Hungarian notation. Because it's a local variable, I guess. As opposed to the //globals that don't really exist in C#. PriceSheet[] lPrcSheets; //snip out a dozen or so other state variables. public string InvokeSoap(methodArgs args) { //do some logging, etc soapClient client = new Client(); client.doThingCompleted += new doThingEventHandler(MyCompletionMethod); client.doThingAsync(args); formhassoapcompleted1 = false; do { string busyWork = ""; } while (formhassoapcompleted1 == false) return lPrcSheets; } private void MyCompletionMethod(object sender, completedEventArgs e) { //In actuality, there's a couple hundred lines of code to transform //SOAP client's class into ours, but I'll have mercy and omit that //so you can see basically how this delegate works. lPrcSheets = e.priceSheets; formhassoapcompleted1 = true; } }
Interestingly, the while loop gets optimized into an infinite loop when you compile for Release. They hadn't noticed it before because the production code was running in Debug mode.
-
lPrcSheets in C# is amazing to me. Like, he seems to think:
- That variable is local (it's scoped to the class, actually)
- You need to use Hungarian in C#
- But the number of characters in a variable name is limited, so you better abbreviate "Price"
do
{
string busyWork = "";
}
while (formhassoapcompleted1 == false)Even if you're gonna do this the rock-stupid way, how about a Thread.Sleep(), idiot? That poor CPU.
Interestingly, the while loop gets optimized into an infinite loop when you compile for Release.
That seems to be a 100% sensible optimization. I don't think the C# compiler is designed to compensate for "another thread came in here and shat all over this thread's variable".
There might be a way to tell C# that's what you expect to happen but, uh. Well. They probably also didn't anticipate someone wanting to "de-async" an already async function.
I have no idea what SOAP library that code's using, but Microsoft's seems to already have a synchronous version: https://msdn.microsoft.com/en-us/library/microsoft.web.services2.messaging.soapsender.send.aspx
-
There might be a way to tell C# that's what you expect to happen but, uh. Well.
volatile
.@blakeyrat said:They probably also didn't anticipate someone wanting to "de-async" an already async function.
They did, which is why they documented (in the Long Long Ago, the Before Times, the Early Days) how to do it with semaphores and WaitOne/WaitAny/WaitAll; they were reluctant to provide a batteries-included solution as inefficient and insufficiently generic. Now the guidance is to use the Task Parallel Library'sTaskCompletionSource
monkeywrench, although you get none of the usual benefits from doing so here.
-
Guess the l might be for list?
-
volatile.
Well there you go. Now is there any code that uses that keyword and isn't also a HUGE WTF-in-progress?
-
Well there you go. Now is there any code that uses that keyword and isn't also a HUGE WTF-in-progress?
Depends on the language. In C++, it's almost always wrong, unless you're writing drivers and your hardware is doing strange stuff.
In Java, it can be useful if you want
AtomicInteger
without the overhead of an actualObject
.I don't know C# too well, maybe the same as Java?
-
I don't think the C# compiler is designed to compensate for "another thread came in here and shat all over this thread's variable".
Yes, but you have to code for it--you use BeginInvoke, Invoke, and EndInvoke. The common use case is exactly for "variables on a UI thread that are altered from a non-UI thread" and most System.Windows.Forms classes have at least some support built-in for it--you'll see comments about some fields and methods being thread-safe vs some that say "only call on a UI thread" or the like.
-
"another thread came in here and shat all over this thread's variable
IF anyone cares, here's the correct way to do it:
https://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
-
In Java, it can be useful if you want AtomicInteger
without the overhead of an actual Objectbut you dont want to do atomic operations with it.Volatile tells the jvm that the variable may be modified by outside processes, it might (I don't think so, but I don't exactly remember or care) allow atomic access (eg of longs on a 32-bit machine, where the vm reads the first half from memory, another process modifies the data, then the vm reads the next half from memory), it definitely doesn't protect against concurrent read-modify-write.
-
unless you're writing drivers and your hardware is doing strange stuff.
Hardware changing the value of a status register is not "strange stuff;" it's very normal for hardware to do.
-
Hardware changing the value of a status register is not "strange stuff;" it's very normal for hardware to do.
I know, but everything hardware does is "strange stuff" to me. ;)
-
it definitely doesn't protect against concurrent read-modify-write
True, what I wrote was BS.
I've seen valid uses of volatile in Java, though. I'll try to find the code again…
-
IF anyone cares, here's the correct way to do it:
You better properly source that SchmuckOverflow link you're about to post.
https://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
Oh, sorry, my mistake, I didn't see the "correct" in your post. Never mind.
-
I've seen valid uses of volatile in Java, though.
Thread A writes, thread B reads. So flagging of things like the processing stage of the code, etc. It's a bit lighter weight than a full lock dance. Best not used with anything larger than an
int
…
-
I think in our case it was a more complex lock-free algorithm. But yeah, volatile works as long as you don't need atomic updates or only one thread writes to the volatile int/bool.
-
```cs
//Ah, love that 'l' Hungarian notation. Because it's a local variable, I guess.Except it's not a local variable.
-
-
@NedFodder said:
Except it's not a local variable.
Well, it was originally. Then it got moved.
Fucking migrants.
-
I think in our case it was a more complex lock-free algorithm. But yeah, volatile works as long as you don't need atomic updates or only one thread writes to the volatile int/bool.
I think I'd have to see some pretty convincing benchmarking before I took a risk on something like that.
-
I think I'd have to see some pretty convincing benchmarking before I took a risk on something like that.
Yup.
What was that saying again? Something like: "There are two kinds of software engineers; those that don't understand multi-threading and those that think they understand multi-threading."
-
@blakeyrat said:
I think I'd have to see some pretty convincing benchmarking before I took a risk on something like that.
Yup.
What was that saying again? Something like: "There are two kinds of software engineers; those that don't understand multi-threading and those that think they understand multi-threading."
There are two kinds of software engineers. Those and that those don't that they understand they multi-threading understand , E_THREAD_ABORT
-
You're ever so lucky if you get E_THREAD_ABORT. That's actually potentially recoverable from…
-
those that don't understand multi-threading
Especially when it's done by components not made by yourself and that causes all sorts of shenanigans that you weren't prepared for...
-
Especially when it's done by components not made by yourself and that causes all sorts of shenanigans that you weren't prepared for...
I've had my fair share of being on the receiving end of those situations as well, yes.