-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Next big C# language should focus on supporting building distributed system and concurrency programming #502
Comments
C# already has asynchronous coroutines in the form of Actual distributed programming can already be solved through libraries such as Akka. |
@uyhung what feature of Java do you feel makes it more suitable than C# in this use case? I'd think it is more about libraries or middleware than language level. |
@uyhung I'm curious if you mentioned Java because of experience working on distributed/concurrent programming in that environment, or if you just listed it as an example. As someone who spent a great deal of time on asynchronous and concurrent (but not so much distributed) programming in Java and C# over the past several years, I came to essentially the opposite conclusion. |
Indeed, Java's advantage here is, at best, ecosystem. The language and JDK, if anything, actively fight you at every step. Having to maintain both a C# WebAPI site as well as a JAX-RS site the experience isn't even remotely comparable. It doesn't help that J2EE is still stuck in Java7 land which is all blocking on futures and callback hell. The experience is somewhat less awful using alternate JVM languages, but not by much. To my knowledge Spring has some support for coroutines via AOP and bytecode rewriters but only at the method level. There's also EA's Orbit library which does something similar. I'm actively working to incorporate the latter into my projects because I find it appalling to have to manually write callback/lambda continuations anymore, but none of that even remotely comes close to just how well Funnily enough probably the best concurrency library on the JVM right now is RxJava. It irks me to no end that Microsoft's own framework is getting more love there than at home. |
@uyhung This should do the same thing as the "A Tour Of Go: Goroutines" example does using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Say(string s)
{
for (var i = 0; i < 5; i++)
{
await Task.Delay(100);
Console.WriteLine(s);
}
}
private static async Task MainAsync(string[] args)
{
var go = Task.Run(() => Say("world"));
var t = Say("hello");
await Task.WhenAll(go, t);
}
public static void Main(string[] args)
{
MainAsync(args).Wait();
}
} edit improved example here #502 (comment) |
@uyhung Here are a couple of things that you might find interesting: |
@sharwell @smoothdeveloper , Hopefully that Microsoft can find a way to make .NET Core a really big ecosystem. |
This repo is specifically for changes to the C# language. If you want to make specific suggestions for inclusions in the BCL to support distributed systems you can try the CoreFX repo. But unless you're expecting Microsoft to build out that ecosystem on their own I'm not sure what you're expecting to happen. Microsoft is providing the environment and trying to expand it's reach. It's up to others to populate the ecosystem. It's not like Sun/Oracle have anything to do with any of those projects that you've mentioned. If anything they flourished due to the massive gaps in functionality left by the JDK. |
You can specify you own custom Task scheduler it just uses the ThreadPool by default to maximise the CPU usage |
It sounds like you don't understand how
|
@sgf |
Do you actually have a concrete proposal to improve the language or are you just here to complain that you don't like C#? If you don't care to listen to how a feature that you are disparaging incorrectly actually works then there is little reason for anyone to attempt to hold a dialog with you. I have nothing against Go's Goroutines. I think that they work quite well in that language. However Go was designed very explicitly around its threading/synchronization model with its baked-in user-mode switching and kernel-mode backing of fibers. I don't believe that attempting to add such to an existing language/framework would be met with as much success since nothing that already exists would understand how the cooperative multithreading would work nor would they understand how the fiber mechanism would work. Any existing code that blocked the kernel thread would block every Goroutine scheduled to be handled by that thread. That is the issue with fiber models in general unless you build everything around that from day one. |
@sgf I can say with very high confidence that there are many people working on or around C# for whom performance is a very important priority across a wide variety of scenarios. For example, there are people like @stephentoub (and to a lesser degree myself) who are specifically interested in improving behavior of multithreaded applications through smarter algorithms (e.g. lock-free approaches), and people like @benaadams who never let us hear the end of it when anything is slow (he's a fun guy to have dinner with and talk about what can be better!). We also have people like @gafter and @jaredpar working on the language itself to both enable and encourage practices with the C# language that lead to better performing and more reliable applications for end users. This is not to say we're the best. Of course I may have an opinion on that, but it's not the point I'm trying to make. What I am saying, is if you believe you've found specific areas where improvements to the language, the runtime, or the tooling will enable end users to have a better-performing product, there will be multiple people eager to consider the ideas regardless of which subsystem they lie in. Just remember that the ecosystem is very large so even when people are working with you to make a change it can take a bit of time to actually ship out. 👍 |
Again, are you planning on actually proposing something here? Your comments do nothing to continue the discussion. In fact I'd go so far to say that they simply demonstrate that you don't understand what you're talking about.
C# has native coroutines. That is what No, they're not the same as goroutines. Only Go has goroutines. The only reason they work in Go is because Go was designed very specifically around them. You cannot take fiber-based cooperative multitasking (a very old concept, by the way) and hack that into existing languages while protecting it from native threading concerns and blocking (which has devastating consequences). You just can't.
How? What does the language offer you here that C# doesn't? Nothing. Absolutely nothing. From a language and framework point of view Java is well over a decade behind C# in terms of distributed and concurrent programming. |
If anyone has any ideas on how to make the language friendlier to distributed and concurrent programming feel free to speak up. |
#88 is somewhat interesting because it removes some of the boiler plate of writing and coordinating event handling logic. Like the inverse of async/await. Not sure what it means to keep all those parameter values around between separate API invocations, and what happens to the object's state when API's calls are not made in the expected order, or the concurrency of the object itself with regards to overlapping calls, etc. Maybe the object is generated with locks that only let new invocations through when the previous initiated sequences are complete. Or maybe if the API's are async, they wouldn't need to block. Interesting to think about. |
The point is to avoid all blocking. The mailbox methods always return instantly, and the chorded methods are always Where it gets interesting is when multiple mailbox methods are used with multiple chorded methods. As I describe you can do this with Dataflow today using |
@HaloFour I'm still trying to get my head around it. I understood it a lot better years ago when C-omega work was ongoing, but now I am having a hard time understanding if this is a generally interesting capability or a narrowly focused one. For instance, in order for all the parameters from all the mailbox functions to be available to the async (chorded) method, each mailbox function can only be called once. That's great make in straight forward to understand how everything combines, but may only map to a narrow subset of interesting dialog/protocol patterns. Even if it doesn't solve everything is it an adequate building block? Not sure yet. I'm trying to model it and it seems like every chord grouping has a lot of expensive data structures going on, so it is a bit deceptive on the cost of making the synchronization work. |
I read from this article I think the key point here is to make any synchronously method can be use as asynchronously method without reimplement it with |
@John0King Go does it by not giving you the option of using dedicated threads, having transferable thread stacks and having all functions interruptable (by runtime); so no function is really sync; even CPU bound ones and you can't control your scheduling (though you can call Simplifying, the .NET gives you more power and control than Go, but can be more complicated as a result. With the most common pitfall being using blocking methods on a threadpool thread; when you don't care about thread ownership; but since you need to be explicit its often missed. You can even serialize the execution of async methods, transfer it to a different machine and then pick up the execution later... |
@sgf: Do you have a concrete proposal on what you would like to see improved? |
Updated the comparison for C#7.1 and example from the "A Tour Of Go: Goroutines" and matched the formatting to better Go version package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
} C#7.1 version using System.Threading.Tasks;
using static System.Console;
using static System.Threading.Tasks.Task;
public class Program
{
static async Task Say(string s) {
for (var i = 0; i < 5; i++) {
await Delay(100);
WriteLine(s);
}
}
static Task Main() => WhenAll(
Run(() => Say("world")),
Say("hello")
);
} |
Kinda late to the party but interesting thread right here. Unfortunately @sgf is unable to clearly articulate his concerns or make constructive proposals for improvement of C#. I personally have had cases where I had to think about the program flow while mixing async/await methods with non-async/await methods but I guess it's probably due to some knowledge deficit on my part. For instance, I just learned lately that using Any suggested resources would be greatly appreciated @HaloFour @mattwar @benaadams. Thanks all! |
What "standard procedure"? If you're comparing something like |
There's yet to be a proposal here. Goroutines can't be implemented in C#. They can't be implemented in any language that isn't Go. Channels can be implemented in libraries and require no language changes. There already is Rx, TPL DataFlow, Akka.NET, and likely many others that fill that need, as well as the concurrent collections in the BCL. |
This is trivially untrue by the principle of Turing Equivalence. A goroutine is really nothing more than a thread, in a language with a built-in syntax keyword for spawning threads. You can already create threads in C#, and Channels are essentially just a blocking queue. There's really nothing there that we don't already have access to. (Yes, it's technically a fiber, but show me one thing you can do with goroutines and channels that you can't trivially accomplish with threads and blocking queues.) |
@masonwheeler I think @HaloFour's point is that the concept of a goroutine exists only in go. Similar or equivalent constructs may exist in other languages but they are not strictly speaking goroutines. I believe the point is that asking for C# to embed a core go concept is the wrong question to ask, instead a proposal for a construct that is native to C# is what is needed to move forward. |
@AlgorithmsAreCool And what new construct would that be? Again, what can Go do that C# can't do just as easily with threads (or Task.Run, if you prefer) and blocking queues? What problem exists here that we need a solution to? |
If async operator operloading was supported, I think we could simply overload some operator like |
C# intentionally makes every |
Sure Using operator overloading we should also be able to make a short syntax for running taks in parallel (like the ~ Say("hello"); go | Say("Hello"); go >> Say("Hello"); |
No, we really couldn't, because that already has a well-defined meaning that it's quite realistic to assume will be used in production code: "less than negative". |
Most of my 'go's are around 'funcs'. So the equivalent in C# (which seems fine with me) is just: using static Task;
//...
Run(() => {
// all the work
}); Which is basically the same as: go func() {
// all the work
}() Note: these have identical character counts, and I'm not sure i see there as being any real need to make this much better. |
Yes, I have to agree it's not much improvement. This one is even more characters: go >> () => {
// all the work
}; This one is a little better: ~() => {
// all the work
}; but I really like this: await (c << x); It would be great to have this come with the Channels API. |
Per the C# spec the second operand of an overloaded bitshift operator must be an |
@HaloFour Probably to prevent exactly this sort of abuse of operator overloads. (See: C++ streams) |
Saying "Run" seems fine to me. I don't see any real value in trying to condense that down any further to a specific character. I mean, it's not like 'go' avoids saying the word 'go' itself. When there's already a very clean and easy way to do things, i don't think there's tremendous value in going overboard on syntactic brevity. |
I agree. I'm drawing my suggestion! :) |
Agreed. 2 mch brvt mks thgs hrd 2 nrstnd! |
@HaloFour Challenge accepted. https://gist.github.com/MI3Guy/aa8491634410beabbdb6bd042a2ca647 |
That's hilarious. |
Wow, that's kind of horrifying! |
“kind of”?? |
I love language wars as much as the next guy, but at what point do we agree that a topic has become an exercise in trolling and bikeshedding and close it... |
@scalablecory Sometimes it is good to have a honeypot. |
So the conclusion is: "No, C# already has async/await, use relevant libraries to do complex scenarios"? Please vote up/down. |
@omidkrad that's not how this works. we vote on specific proposals, not vague unsubstantiated musings. |
Microsoft once had CCR/DSS toolkit for programming concurrent/distributed software services but it was not easy to use for the average developer. With async/await, C# has come a long way from there but I still believe it should be a lot easier to create concurrent and distributed software. If distributed software is more natively supported in the language/framework that would set the standard for the developers to follow, just as async/await does for asynchronous code. |
What actually is the suggestion in this thread? If there is one, is it any different from what Rx.NET already offers? |
@FrankSzendzielarz There isn't a specific suggestion, just a suggestion of what kind of thing should be suggested. |
Getting a little meta, are we? 😛 |
Indeed they are, and it'll be an interesting experiment. I think the Java world may be a little more insulated from this given the vast majority of the ecosystem does go through the JRE so theoretically the majority of places where a thread might block can be updated to properly support virtual threading. All third party native code will have to be updated. Blocking the underlying OS thread will always be possible and potentially severely reduce the concurrency of the virtual threads, especially since by default they all share the same common fork/join pool. And I'll be really curious if they'll have a mechanism to detect deadlocking like Go has, probably not. What will be particularly interesting is that Java mixes OS threads and virtual threads, and use of the latter must be deliberate. I did a LabWeek project with the early access bits of Loom last month and it was a lot of fun. I particularly enjoyed the continuation primitives which allow you to write generators/async without language modifications. And I was pretty impressed with how far virtual threads have been implemented so far. I even used a |
Thank you for the reply. Those experiments are quite interesting! I haven't experimented with a Loom JVM branch yet, so I was not aware that you could write generators without the overhead of spawning a separate fiber, as what you'd do in go. Would you mind sharing the rest of the code in the |
Here are those two files. Being a LabWeek project they're a little sloppy, the goal being just to get them to work enough to demonstrate the concepts. Enjoy! |
What difference between |
Loom is an implementation of delimited continuations in the Java runtime. That allows you to capture the execution context and stack of a thread into a variable (the functional interface The argument for a model like Loom or Go is that the only real reason for async APIs is to avoid blocking threads, and the only reason to avoid blocking threads is that threads are expensive. If threads were very cheap then there is much less of a reason to have async APIs or async-specific language features. Java put out another early access build in July and they've stabilized the implementation quite a bit. I was able to spin up 2 million virtual threads and "block" them all on a shared lock with a single backing thread and all within 2 GB of memory. 2 million real threads in both Java and C# would require 2 TB of memory, even if they were all blocked and not doing anything. The tricky bit is that "compatible ecosystem" as anything under the hood that blocks needs to be modified to understand how to yield the virtual thread and queue up completion to resume that virtual thread on some other backing thread. Accidentally blocking real threads can starve all of the virtual threads from being scheduled. Java is taking a little bit of a gamble that they can manage all of the points where blocking can occur from within the runtime and that third-part native implementations of I/O libraries that could block can be expected to be updated. |
There's been a lot of good conversations on this thread. However, as there's no real language proposal here, I'm going to close this out. Feel free to continue using it for discussion if you want to. |
The next version of C# language should be focus on making it easier in building distributed system and concurrency programming instead of introducing more syntax sugar. That's the only way make C# really a big programming language in comparing with the others such as Java.
One of the feature I think is a native support to implement Golang's co-routines.
The text was updated successfully, but these errors were encountered: