There's no such thing as single-threaded async i/o on the CLR because the callback will always be called on an i/o completion thread of the thread pool. The EAP pattern (as implemented by WebClient and others) is kind enough to automatically schedule the event raising using SynchronizationContext.Current, but APM and TAP won't do this for you on their own.
I find that odd since you can do single-threaded async i/o on Linux using libevent. Are you sure this can't be done on .NET? Single threaded programs are easier to reason about since you don't have to worry about locking writes to shared data.
You can always do the rescheduling yourself using SynchronizationContext.Send/Post, Task.ContinueWith(..., TaskScheduler.FromCurrentSynchronizationContext()) or do! Async.SwitchToContext in an async computation expression.
/edit: If I'm not mistaken (no F# in reach to test it;) ), under certain circumstances async expressions will inherit the SynchronizationContext and use it to schedule the continuation - just as the C# 5.0 CTP does:
1 2 3 4
async { let! response = request.GetResponseAsync() doSomethingWith response // still on UI thread } |> Async.StartImmediate
It might be clearer if I posted the code I have. I did try to figure out how to do what I wanted using the Task classes but couldn't. Note that I don't want to use async workflows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
open System type RequestState = { request : Net.WebRequest; url: string } let get_response_cb (result : IAsyncResult) = let tid = string(Threading.Thread.CurrentThread.ManagedThreadId) let rs = result.AsyncState :?> RequestState use response = rs.request.EndGetResponse(result) Console.WriteLine ("Thread id = " + tid + ", Response for " + rs.url) use stream = response.GetResponseStream() use reader = new System.IO.StreamReader(stream) let html = reader.ReadToEnd() () let begin_get_response (url : string) = let wr = Net.WebRequest.Create url let cb = new AsyncCallback(get_response_cb) let result = wr.BeginGetResponse(cb, {request = wr; url = url}) let tid = string (Threading.Thread.CurrentThread.ManagedThreadId) Console.WriteLine ("Thread id = " + tid + ", Request for " + url) () begin_get_response "http://www.google.com"
In this setup begin_get_response can be called multiple times within a loop without it blocking while it waits for a response because the response is delegated to a callback. In the .NET framework the callback function is called run within a different thread that is created implicitly. The question is how to convert these functions so that the callback is run within the same thread as the rest of the program.
About the indentation:
I tried to fix it but couldn't. begin_get_response and get_response_cb are functions so need indentation. The type RequestState needs indentation as well.
Here's a simple WinForms app that demos it. Async.Parallel starts a bunch of asyncs in the threadpool, but the first thing each async does is return to the SynchronizationContext (UI thread) and so all its work happens on the UI thread.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
open System open System.Net open System.Threading open System.Windows.Forms let urls = [ "http://bing.com"; "http://cnn.com" ] let form = new Form() let textBox = new TextBox(Text = "loading pages...", Multiline=true, Width=300, Height=300) form.Controls.Add(textBox) form.Load.Add(fun _ -> let ctxt = SynchronizationContext.Current seq { for u in urls do yield async { do! Async.SwitchToContext(ctxt) textBox.Text <- textBox.Text + sprintf "\r\n(%d) about to start fetching %s" Thread.CurrentThread.ManagedThreadId u let wr = WebRequest.Create(u) let! resp = wr.AsyncGetResponse() textBox.Text <- textBox.Text + sprintf "\r\n(%d) %s: %d" Thread.CurrentThread.ManagedThreadId u resp.ContentLength } } |> Async.Parallel |> Async.Ignore |> Async.Start ) [<STAThread>] do Application.Run(form)
Thanks but that's still technically multithreaded. Each async workflow will intiialy start in it's own thread allocated from the thread pool. This is why I specified that I did not want to use async workflows. I realize that the sample posted is logically single threaded but any correct multi-threaded program will be logically single threaded.
I'm just after an example single-threaded program that demonstrates asynchronous i/o on .NET. It doesn't have to use a WebRequest to do it if the WebRequest class makes it too hard to do. It can use another class, like the Socket class, if that class makes it easier. This model, i.e. single threaded async i/o, for writing programs is in use on older operating systems that don't support threads.
Thanks for the clarification.
What's wrong with a single async and StartImmediate then?
1 2 3 4 5
async { blah blah sync let! html = AsyncWebWhatever more sync } |> Async.StartImmediate
all runs entirely on the UI thread. The Async call returns all the way back down to the message pump, and when the IO returns, it posts work to the pump which restores this context and keeps going.
Thanks, that's great. StartImmediate did the trick and let me have single-threaded program with multiple request in flight at once.
May have spoken too soon ... When the response is returned the original context is not restored.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
let st_request (url : string) max_urls (count : int ref) (finished : Threading.ManualResetEvent) = async { let tid = string(Threading.Thread.CurrentThread.ManagedThreadId) let wr = Net.WebRequest.Create url Console.WriteLine("Thread id = " + tid + ", Request for " + url) use! response = wr.AsyncGetResponse() count := !count + 1 let tid = string(Threading.Thread.CurrentThread.ManagedThreadId) Console.WriteLine("Thread id = " + tid + ", Response for " + url) if !count = 6 then finished.Set() |> ignore () } ///run the single threaded asynchronous version let sa_run = let finished = new Threading.ManualResetEvent(false) let count = ref 0 let ctxt = Threading.SynchronizationContext.Current (fun (urls : list<string>) -> Console.WriteLine("Single-threaded asynchronous requests ...") List.iter (fun url -> Async.StartImmediate(st_request url urls.Length count finished)) urls finished.WaitOne() |> ignore)
yields the following output:
1 2 3 4 5 6 7 8 9 10 11 12 13
Single-threaded asynchronous requests ... Thread id = 1, Request for http://www.google.com Thread id = 1, Request for http://www.microsoft.com Thread id = 1, Request for http://www.yahoo.com Thread id = 1, Request for http://www.wordpress.com Thread id = 1, Request for http://www.blizzard.com Thread id = 1, Request for http://www.valvesoftware.com Thread id = 13, Response for http://www.microsoft.com Thread id = 13, Response for http://www.yahoo.com Thread id = 14, Response for http://www.valvesoftware.com Thread id = 10, Response for http://www.google.com Thread id = 10, Response for http://www.blizzard.com Thread id = 13, Response for http://www.wordpress.com
This technique only works if you are starting on a thread with a synchronization context and a message pump, e.g. the UI thread in a WPF/WinForms app. It sounds like maybe you're just doing this straight from a console app at the top-level?
Yeah that's right - it's just in the console.
I am very interested in this, too. Did you ever figure it out?
The code I posted earlier (with Async.StartImmediate on the STA UI thread) does it. You have to have a message pump, else it doesn't make much sense, really.
In addition, is it possible to use MailboxProcessors in a single-threaded async manner?
Thanks, Brian. Does that make sense for a console app or a Windows Service? Is there another way to create a "message pump" other than a WinForms app?
I'm interested in leveraging F#'s async for a performant server platform. My interest in single threading is related to this series of responses on Twitter:
1 2 3
http://twitter.com/bvanderveen/status/17683108828549120 http://twitter.com/bvanderveen/status/17683439473926144 http://twitter.com/bvanderveen/status/17683861236355073
Is that logic flawed? Is the difference marginal?
Thanks,
Ryan
If it was such a bad idea, why would Microsoft ever have implemented Async Pages? If half of the processing time of requests is IO-bound, using async IO and a threadpool you can achieve a ratio of one thread per two requests - sounds like a good idea to me.
Every async wrapper function in the F# library (e.g. AsyncSleep, AsyncGetResponse, etc) will capture and return you to a synchronization context, if there is one.
(If you build your own asyncs out of primitives like FromBeginEnd or FromContinuations, then it's up to you to do this on your own if you want that behavior.)
Also note that all asyncs are 'logically-single-threaded' (unless doing explicit concurrency like Async.Parallel), it's just that you may hop from e.g. threadpool thread 1 to threadpool thread 2 at 'bang' points.
Topic tags
- f# × 3705
- websharper × 1897
- compiler × 286
- functional × 201
- ui next × 139
- c# × 121
- classes × 97
- web × 97
- .net × 84
- book × 84
- async × 76
- ui.next × 67
- bug × 54
- core × 49
- website × 49
- server × 45
- parallel × 43
- ui × 43
- enhancement × 41
- parsing × 41
- testing × 41
- trywebsharper × 41
- typescript × 37
- html × 35
- javascript × 35
- owin × 35
- asynchronous × 30
- monad × 28
- ocaml × 28
- tutorial × 27
- warp × 27
- haskell × 26
- sitelet × 25
- linq × 22
- workflows × 22
- wpf × 20
- fpish × 19
- introduction × 19
- silverlight × 19
- sitelets × 19
- monodevelop × 17
- rpc × 17
- suave × 17
- piglets × 16
- collections × 15
- feature request × 15
- jquery × 15
- templates × 15
- getting started × 14
- pipeline × 14
- kendoui × 13
- reactive × 12
- 4.1.0.171 × 11
- monads × 11
- opinion × 10
- 4.0.190.100-rc × 9
- deployment × 9
- fixed × 9
- formlets × 9
- in × 9
- json × 9
- plugin × 9
- proposal × 9
- scheme × 9
- solid × 9
- basics × 8
- concurrent × 8
- highcharts × 8
- how-to × 8
- python × 8
- 4.1.1.175 × 7
- complexity × 7
- documentation × 7
- visual studio × 7
- 4.1.2.178 × 6
- lisp × 6
- real-world × 6
- released in 4.0.192.103-rc × 6
- remoting × 6
- resources × 6
- scala × 6
- websharper ui.next × 6
- workshop × 6
- xaml × 6
- 4.0.193.110 × 5
- 4.2.3.236 × 5
- aspnetmvc × 5
- authentication × 5
- azure × 5
- bootstrap × 5
- conference × 5
- dsl × 5
- formlet × 5
- java × 5
- list × 5
- metaprogramming × 5
- ml × 5
- released in Zafir.4.0.188.91-beta10 × 5
- sql × 5
- visualstudio × 5
- websharper.forms × 5
- zafir × 5
- 4.0.192.106 × 4
- 4.0.195.127 × 4
- 4.1.0.38 × 4
- 4.2.1.86 × 4
- 4.2.6.118 × 4
- css × 4
- example × 4
- extensions × 4
- fsi × 4
- fsx × 4
- html5 × 4
- jqueryui × 4
- lift × 4
- reflection × 4
- remote × 4
- rest × 4
- spa × 4
- teaching × 4
- template × 4
- websocket × 4
- wontfix × 4
- 4.0.196.147 × 3
- 4.1.0.34 × 3
- 4.1.6.207 × 3
- 4.2.1.223-beta × 3
- 4.2.11.258 × 3
- 4.2.4.114 × 3
- 4.2.4.247 × 3
- 4.2.5.115 × 3
- 4.2.6.253 × 3
- 4.2.9.256 × 3
- ajax × 3
- alt.net × 3
- aml × 3
- asp.net mvc × 3
- canvas × 3
- cloudsharper × 3
- compilation × 3
- database × 3
- erlang × 3
- events × 3
- extension × 3
- file upload × 3
- forums × 3
- inline × 3
- issue × 3
- kendo × 3
- macro × 3
- mono × 3
- msbuild × 3
- mvc × 3
- pattern × 3
- piglet × 3
- released in Zafir.4.0.187.90-beta10 × 3
- svg × 3
- type provider × 3
- view × 3
- 4.1.1.64 × 2
- 4.1.5.203 × 2
- 4.1.7.232 × 2
- 4.2.10.257 × 2
- 4.2.3.111 × 2
- 4.2.5.249 × 2
- android × 2
- asp.net × 2
- beginner × 2
- blog × 2
- chart × 2
- client × 2
- client server app × 2
- clojure × 2
- computation expressions × 2
- constructor × 2
- corporate × 2
- courses × 2
- cufp × 2
- d3 × 2
- debugging × 2
- direct × 2
- discriminated union × 2
- docs × 2
- elm × 2
- endpoint × 2
- endpoints × 2
- enterprise × 2
- entity framework × 2
- event × 2
- f# interactive × 2
- fable × 2
- flowlet × 2
- formdata × 2
- forms × 2
- fsc × 2
- google maps × 2
- hosting × 2
- http × 2
- https × 2
- iis 8.0 × 2
- install × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- jquery mobile × 2
- keynote × 2
- lens × 2
- lenses × 2
- linux × 2
- listmodel × 2
- mac × 2
- numeric × 2
- oauth × 2
- obfuscation × 2
- offline × 2
- oop × 2
- osx × 2
- packaging × 2
- pattern matching × 2
- performance × 2
- pipelines × 2
- q&a × 2
- quotation × 2
- reference × 2
- released in Zafir.4.0.185.88-beta10 × 2
- rx × 2
- script × 2
- security × 2
- self host × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- tutorials × 2
- type × 2
- url × 2
- var × 2
- websharper.charting × 2
- websharper4 × 2
- websockets × 2
- wig × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- 4.0.194.126 × 1
- 4.1.3.184 × 1
- 4.1.4.189 × 1
- 4.2.0.214-beta × 1
- 4.2.12.259 × 1
- 4.2.2.231-beta × 1
- 4.2.8.255 × 1
- Canvas Sample Example × 1
- DynamicStyle Animated Style × 1
- Fixed in 4.0.190.100-rc × 1
- Released in Zafir.UI.Next.4.0.169.79-beta10 × 1
- SvgDynamicAttribute × 1
- WebComponent × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- actor × 1
- addin × 1
- agents × 1
- aggregation × 1
- agile × 1
- alter session × 1
- animation × 1
- anonymous object × 1
- apache × 1
- api × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net core × 1
- asp.net integration × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- attributes × 1
- authorization × 1
- b-tree × 1
- back button × 1
- badimageformatexception × 1
- bash script × 1
- batching × 1
- binding-vars × 1
- bistro × 1
- body × 1
- bundle × 1
- camtasia studio × 1
- cas protocol × 1
- charts × 1
- clarity × 1
- class × 1
- cli × 1
- clipboard × 1
- clojurescript × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- color zones × 1
- combinator × 1
- combinators × 1
- compile × 1
- compile code on server × 1
- config × 1
- confirm × 1
- content × 1
- context × 1
- context.usersession × 1
- continuation-passing style × 1
- coords × 1
- cordova × 1
- cors × 1
- coursera × 1
- cross-domain × 1
- csla × 1
- current_schema × 1
- custom content × 1
- data × 1
- data grid × 1
- datetime × 1
- debug × 1
- declarative × 1
- delete × 1
- devexpress × 1
- dhtmlx × 1
- dictionary × 1
- directattribute × 1
- disqus × 1
- distance × 1
- do binding × 1
- doc elt ui.next upgrade × 1
- docker × 1
- dojo × 1
- dol × 1
- dom × 1
- domain × 1
- du × 1
- duf-101 × 1
- dynamic × 1
- eastern language × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- enums × 1
- error × 1
- etw × 1
- euclidean × 1
- eventhandlerlist × 1
- examples × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- failed to translate × 1
- fake × 1
- fantomas × 1
- fear × 1
- float × 1
- form × 1
- form-data × 1
- forum × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- fsunit × 1
- function × 1
- functional style × 1
- game × 1
- games × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- getting-started × 1
- google × 1
- google.maps × 1
- grid × 1
- group × 1
- guide × 1
- hash × 1
- headers × 1
- hello world example × 1
- heroku × 1
- highchart × 1
- history × 1
- how to × 1
- html-templating × 1
- http405 × 1
- httpcontext × 1
- hubfs × 1
- i18n × 1
- ie 8 × 1
- if-doc × 1
- iis × 1
- image × 1
- images × 1
- inheritance × 1
- initialize × 1
- input × 1
- install "visual studio" × 1
- installer × 1
- int64 × 1
- interfaces × 1
- internet explorer × 1
- interop × 1
- interpreter × 1
- io × 1
- iobservable × 1
- ios × 1
- iot × 1
- ipad × 1
- isomorphic × 1
- javascript optimization × 1
- javascript semanticui resources × 1
- jquery-plugin × 1
- jquery-ui × 1
- jquery-ui-datepicker × 1
- js × 1
- kendo datasource × 1
- kendochart × 1
- kendoui compiler × 1
- knockout × 1
- l10n × 1
- learning × 1
- library × 1
- libs × 1
- license × 1
- licensing × 1
- lineserieszonescfg × 1
- local setting × 1
- localization × 1
- logging × 1
- loop × 1
- macros × 1
- mailboxprocessor × 1
- mapping × 1
- maps × 1
- markerclusterer × 1
- markup × 1
- marshal × 1
- math × 1
- mathjax × 1
- message × 1
- message passing × 1
- message-passing × 1
- meta × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- mix × 1
- mobile installation × 1
- mod_mono × 1
- modal × 1
- module × 1
- mouseevent × 1
- mouseposition × 1
- multidimensional × 1
- multiline × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- native × 1
- nested × 1
- nested loops × 1
- node × 1
- nunit × 1
- object relation mapper × 1
- object-oriented × 1
- om × 1
- onboarding × 1
- onclick × 1
- optimization × 1
- option × 1
- orm × 1
- os x × 1
- output-path × 1
- override × 1
- paper × 1
- parameter × 1
- persistence × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- post × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- privacy × 1
- private × 1
- profile × 1
- programming × 1
- project × 1
- project euler × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- proxy × 1
- ptvs × 1
- public × 1
- pure f# × 1
- purescript × 1
- qna × 1
- quant × 1
- query sitelet × 1
- question × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- reactjs × 1
- real-time × 1
- ref × 1
- region × 1
- released in 4.0.190.100-rc × 1
- reporting × 1
- responsive design × 1
- rest api × 1
- rest sitelet × 1
- restful × 1
- round table × 1
- router × 1
- routing × 1
- rpc reverseproxy × 1
- runtime × 1
- sales × 1
- sample × 1
- sampleapp × 1
- scriptcs × 1
- scripting × 1
- search × 1
- self hosted × 1
- semanticui × 1
- sequence × 1
- serialisation × 1
- service × 1
- session-state × 1
- sharepoint × 1
- signals × 1
- sitelet website × 1
- sitelet.protect × 1
- sitlets × 1
- slickgrid × 1
- source code × 1
- sqlentityconnection × 1
- ssl × 1
- standards × 1
- static content × 1
- stickynotes × 1
- streamreader × 1
- stress × 1
- strong name × 1
- structures × 1
- submitbutton × 1
- subscribe × 1
- svg example html5 websharper.ui.next × 1
- sweetalert × 1
- system.datetime × 1
- system.reflection.targetinvocationexception × 1
- table storage × 1
- targets × 1
- tdd × 1
- templates ui.next × 1
- templating × 1
- text parsing × 1
- three.js × 1
- time travel × 1
- tls × 1
- tooltip × 1
- tracing × 1
- tsunamiide × 1
- turkish × 1
- twitter-bootstrap × 1
- type erasure × 1
- type inference × 1
- type providers × 1
- type-providers × 1
- typeprovider × 1
- ui next forms × 1
- ui-next × 1
- ui.next jqueryui × 1
- ui.next charting × 1
- ui.next formlets × 1
- ui.next forms × 1
- ui.next suave visualstudio × 1
- ui.next templating × 1
- unicode × 1
- unittest client × 1
- upload × 1
- usersession × 1
- validation × 1
- vb × 1
- vb.net × 1
- vector × 1
- view.map × 1
- visal studio × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio 2012 × 1
- visual studio shell × 1
- vs2017 compiler zafir × 1
- vsix × 1
- web api × 1
- web-scraping × 1
- webapi × 1
- webcomponents × 1
- webforms × 1
- webgl × 1
- webrtc × 1
- webshaper × 1
- websharper async × 1
- websharper codemirror × 1
- websharper f# google × 1
- websharper forms × 1
- websharper reactive × 1
- websharper rpc × 1
- websharper sitelets routing × 1
- websharper warp × 1
- websharper-interface-generator × 1
- websharper.chartsjs × 1
- websharper.com × 1
- websharper.exe × 1
- websharper.owin × 1
- websharper.ui.next × 1
- websharper.ui.next jquery × 1
- websockets iis × 1
- why-websharper × 1
- windows 7 × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- www.grabbitmedia.com × 1
- xamarin × 1
- xml × 1
- yeoman × 1
- yield × 1
- zafir beta × 1
- zafir websharper4 × 1
- zarovizsga × 1
![]() |
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy |
Built with WebSharper |
Hi
How does one write single-threaded program to do a single-threaded asynchronous io? I've tried by writing a program to make http requests to a url using WebRequest.BeginGetResponse, registering a callback delegate using AsyncCallback. However my callback function is run within a thread with a different thread id from the main thread. The context of this is that I'm just writing up an example program that initiates and receives a bunch of web requests using the following methods: 1) single-threaded synchronous i/o, 2) multi-threaded synchronous i/o, 3) single-threaded async i/o and 4) multi-threaded async i/o. It's number 3 that I can't seem to find out how to do in .NET.