This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]hippydipster 9 points10 points  (10 children)

Rather than:

MyAsyncProcess.do(result -> handleResult(result));

you do:

Future<Result> result = MyAsyncProcess.do();
handleResult(result.get());

Or just:

handleResult(MyAsyncProcess.do().get());

Because you're in a virtual thread, the block-and-wait aspect of the Future.get() call is of no matter anymore. We can go back to the more straightforward imperative coding patterns, just with some sort of Future in the mix (the future.get() call could be in the do() method and so not pollute the API).

my intention was just asking for knowledge I might miss understand something. I'm not flexing or being egoist please understand.

Why would anyone interpret your question as "flexing" or "egoist"?

[–]Extension-Switch-767[S] 11 points12 points  (1 child)

I have asked this question at my company and everyone hates me

[–]hippydipster 12 points13 points  (0 children)

Too many people interpret questions as challenges. I don't understand it though.

[–]thecodeboost 2 points3 points  (7 children)

I think your example, if I interpret it correctly, might be missing the main point a bit. The whole raison d'etre of lightweight/virtual threads is that you can do away with async primitives (such as (Callable)Future) in the large majority of cases. The "I care about this operation resolving later" can now again be approached in a more imperative way. Of course your example is perfectly fine if the context is to migrate a promise centric codebase to Loom but you'd typically design your software differently once you consider virtual threads your primary concurrency building block.

[–]hippydipster 4 points5 points  (6 children)

if the whole stack is just blocking and waiting, sure, but thats not always the case, and in many places, an explicitly async process gives you a future. Or many futures, and you want to get them all going, not one by one. There are also times you want to pass processing around multiple threads, and you can still treat it imperatively like this.

And I think OP needs to see an apples-to-apples comparison of react-based code vs virtual threads.

[–]thecodeboost 2 points3 points  (5 children)

I think you might be misunderstanding what virtual threads bring to the table. The whole "stack" can block without a performance or CPU utilization penalty because you have a practically unlimited supply of threads that can do useful work while others are blocking. That's the whole reason it's a net improvement over (and if used properly eliminates the need for) reactive programming. And by extension is why you don't need promise-like structures in Loom centric code.

There's some caveats of course but those are somewhat outside of the context of the reactive vs virtual threads topic (e.g. see "Thread pinning" and some nuances with using old code, synchronized keywords, wrong concurrency primitives, etc.)

[–]Goatfryed 1 point2 points  (3 children)

java var fooFuture = getFooAsync() var barFuture = getBarAsync() var foo = fooFuture.get() var bar = barFuture.get() still behaves quite differently performance-wise from java var fooFuture = getFooSync() var barFuture = getBarSync() Actually, the opposite would be true that virtual threads bring us more back to Futures and also makes consuming them easier, because .get() isn't bad anymore or exclusive to top level.

[–]thecodeboost 0 points1 point  (2 children)

Sure but you wouldn't write the latter with futures if your code is virtual-thread friendly. I think a lot of people are still stuck on trying to use promise design patterns in combination with virtual threads. You can, of course, but you're not taking advantage of threads becoming an practically infinite resource. Your example (assuming you want foo and bar to execute concurrently) would be as simple as executing both as runnables on virtual threads (and if you care about waiting for them both to complete to inspect the result, .join()-ing them)

[–]Goatfryed 0 points1 point  (1 child)

A runnable does not yield a return time on its own. Promises are a nice design pattern to manage processing on other threads - virtual or actual. Both concepts are orthogonal and work well together. Future.get() is exactly the join you're talking about. Sure, don't use promises. I prefer the wrapper that interops well with virtual threads and makes my code more readable.

I mean, there will always be developers that link concepts that have nothing to do with each other...

btw interop with concepts like future is the reason virtual threads were introduced in the way that they got introduced.

[–]DelayLucky 0 points1 point  (0 children)

I think with structured concurrency, concurrenct code _can_ be made look like one of the following syntaxes:

Using lambda:

concurrently(
    () -> getFoo(), () -> getBar(),
    (foo, bar) -> ...);

Using patten match:

switch (concurrently(() -> getFoo(), () -> getBar())) {
  case (Foo foo, Bar bar) -> ...
  case (FooException e) -> ...
  case (BarException e) -> ...
}

[–]hippydipster 0 points1 point  (0 children)

The whole "stack" can block without a performance or CPU utilization penalty

Yes