What is an example of a hard programming challenge that has an elegant solution in Go but not others languages?

xuanbao · · 472 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I am thinking a problem that is easy to solve with Go but not others will need to make use of channel and go routines. But I can&#39;t seem to think of one. </p> <hr/>**评论:**<br/><br/>cdoxsey: <pre><p>TCP Proxy</p> <pre><code>func run() error { li, err := net.Listen(&#34;tcp&#34;, &#34;127.0.0.1:5000&#34;) if err != nil { return err } defer li.Close() for { conn, err := li.Accept() if err != nil { return err } go proxy(&#34;127.0.0.1:5001&#34;, conn) } } func proxy(destaddr string, src net.Conn) error { defer src.Close() dst, err := net.Dial(&#34;tcp&#34;, destaddr) if err != nil { return err } defer dst.Close() errs := make(chan error, 2) go func() { _, err := io.Copy(dst, src) errs &lt;- err }() go func() { _, err := io.Copy(src, dst) errs &lt;- err }() return &lt;-errs } </code></pre> <p>Example:</p> <pre><code># start the proxy [1] go run main.go # run sh listening on port 5001 [2] netcat -l -p 5001 -e /bin/sh # connect to port 5000 [3] netcat 127.0.0.1 5000 [4] echo hi =&gt; hi </code></pre> <p>Simple and efficient.</p></pre>tv64738: <pre><p>Psst, <a href="https://godoc.org/golang.org/x/sync/errgroup">https://godoc.org/golang.org/x/sync/errgroup</a></p></pre>kisielk: <pre><p>Whoa sweet library. Somehow haven&#39;t heard of it before. </p></pre>jeremiahs_bullfrog: <pre><p>I got tired of dealing with our ISP for doing port forwards and I didn&#39;t want to mess with iptables, so I built one in Go. I wrote a simple file format and had a basic port forwarder built in 100 LOC or so and it only took an hour or so and it&#39;s been running solid for months now.</p> <p>That&#39;s what I love about Go. If threading and sockets are involved in large quantities, Go will be quick and easy.</p> <p>If anyone cares, I can dig up the code, but honestly it would probably be quicker to just rewrite it since it&#39;s such a simple thing. In fact, I think every Go programmer should build a port forwarder while learning since it&#39;s so simple and useful.</p></pre>tebeka: <pre><p>Any blocking operation with a timeout. Which is go is easily done by &#34;select&#34;</p> <pre><code>ch := make(chan *response) go fetch(url, ch) select { case resp &lt;- ch: handleResponse(resp) case &lt;- time.After(100 * time.Millisecond): fmt.Println(&#34;timeout&#34;) } </code></pre></pre>bascule: <pre><p>Erlang:</p> <pre><code>wait_for_onhook() -&gt; receive onhook -&gt; disconnect(), idle(); {connect, B} -&gt; B ! {busy, self()}, wait_for_onhook() after 60000 -&gt; disconnect(), error() end. </code></pre></pre>spaghettiCodeArtisan: <pre><p>Rust version:</p> <pre><code>fn main() { let (tx, rx) = channel(); thread::spawn(move || { // Something is being done or waited for here... tx.send(result); }); match rx.recv_timeout(Duration::from_millis(100)) { Ok(resp) =&gt; handleResponse(resp), Err(Timeout) =&gt; println!(&#34;Timeout&#34;), Err(Disconnect) =&gt; println!(&#34;Channel hung up&#34;), } } </code></pre></pre>w2qw: <pre><p>go&#39;s advantage there&#39;s is that green threads are more lightweight. Having said largely I think it tends to be preferred to use socket timeouts.</p></pre>spaghettiCodeArtisan: <pre><blockquote> <p>go&#39;s advantage there&#39;s is that green threads are more lightweight. </p> </blockquote> <p>Well, yeah, obviously. I thought the objective was the wait with timeout. If the objective is coroutine demonstration, similar thing can be done in Rust with Futures... (Although I&#39;m personally not a huge fan of that approach.)</p></pre>Thaxll: <pre><p>Good luck spawning 1000s of them.</p></pre>slowratatoskr: <pre><p>bruh erlang has been spawning thousands of &#34;goroutines&#34; before that term was even coined.</p></pre>Thaxll: <pre><p>We&#39;re talking about Rust here.</p></pre>spaghettiCodeArtisan: <pre><blockquote> <p>Good luck spawning 1000s of them.</p> </blockquote> <p>Not sure what the point of this comment is since the same one has already been written and anwsered...</p> <p>I thought the difference between a native thread and a green thread was obvious.</p></pre>Thaxll: <pre><p>If it&#39;s different then why post the solution in Rust? It looks the same but performance wise it&#39;s completely different so your example doesn&#39;t answer OP.</p></pre>distark: <pre><p>bash (on a vaguely gnu userland).. I know it&#39;s not amazing and I&#39;m very drunk at a wedding but I thought I&#39;d throw this in FWIW</p> <pre><code>resp=&#34;$(timeout 1s curl -q https://blabla.com)&#34; if [ &#34;x$resp&#34; == x ]; then echo fail else echo ${resp} fi </code></pre></pre>_ar7: <pre><p>JS</p> <pre><code>const response = await Promise.race( fetch(url), new Promise(r =&gt; setTimeout(r, 100)) ) if (response) { handleResponse(response) } else { console.log(&#39;timeout&#39;) } </code></pre></pre>mcouturier: <pre><p>2 hours and 798 dependencies fetched later...</p></pre>_ar7: <pre><p>lol not necessarily for this example, but regardless, it was just show the same idea in a different language.</p></pre>mcouturier: <pre><p>No worries I was just depicting my own experience :) Nice example!</p></pre>DerNalia: <pre><p>JS is getting some interesting features recently. See: <a href="http://ember-concurrency.com/#/docs/task-function-syntax" rel="nofollow">http://ember-concurrency.com/#/docs/task-function-syntax</a></p></pre>Creshal: <pre><p>And in five years, enough people will use a compatible browser!</p></pre>DerNalia: <pre><p>maybe. lol</p></pre>spaghettiCodeArtisan: <pre><p>You&#39;re being overly optimistic there...</p></pre>campbellm: <pre><p>Is this a different kind of select than C&#39;s?</p></pre>LimEJET: <pre><p>C&#39;s select is an OS-level feature, while Go&#39;s is part of the language runtime and is more of a parallel switch.</p></pre>ericzhill: <pre><p>Yes*</p></pre>biskit10: <pre><p>Wouldn&#39;t it be easier to just google &#34;golang select&#34; than wait for a response on Reddit?</p></pre>0x6c6f6c: <pre><p>Wouldn&#39;t it be easier to let other people decide if they&#39;d like to answer and not shit on someone for simply asking a question?</p></pre>samuellampa: <pre><p>I have one, which I&#39;m to this day trying to find if possible to implement equally elegantly in any other language: A scientific workflow management system, which uses a simple model of processes (simply goroutines packaged in structs, with channels for communication stored in struct fields aka &#39;ports&#39;) and channels for the communication, to get a nice &#34;implicit scheduler&#34; for managing things like HPC jobs, using very little code (I counted that it uses 3x less code than a recent python based system in the same category, by a respected bioinfo org).</p> <p>My library is open source and available at <a href="http://scipipe.org">http://scipipe.org</a> and we are just starting to use it in production for coordingating our HPC jobs at <a href="http://pharmb.io">http://pharmb.io</a> right now.</p></pre>chmikes: <pre><p>Thanks for sharing the source code. I have to implement a scientific pipeline with external programs. I will look into your package. The documentation is very nice. </p> <p>Are the different tasks executing asynchronously ? </p> <p>We would also like to use a database to store progress information and intermediate results for external monitoring. What do you use for monitoring ? </p></pre>samuellampa: <pre><p>Thanks for the kind words! Yes, the tasks are executed asynchronously, which is exactly what would be so hard to do without Go-routines. For example in Luigi which we used before, you needed separate full python processes (workers) for each concurrent task.</p> <p>(More details, perhaps too much: Since we used tasks to keep track of HPC jobs live (with a blocking system call to the SLURM HPC resource manager), we quickly ran into a limit of how many processes could reasonably be running on the scheduling node, even if they did not do any actual work (that would be done on other HPC nodes). I think we maxed out on like 64 processes, while we wanted thousands, so that our thousands of tasks could be queued up in the HPC system as fast as possible)</p> <p>Regarding monitoring, that is a good question. We don&#39;t really have much in that area yet, except from the audit logging to stdout and manually keeping track of which files are produced, but is something we&#39;ve been thinking about, and for sure would like to improve upon. Creating web UIs and APIs in Go is easy, so I hope we can come up with something soon.</p></pre>asyncrep: <pre><p>For monitoring, what about something like Graylog or Kibana? Complement it with Prometheus/Grafana and you&#39;ll have a very powerful setup.</p></pre>novabyte: <pre><p>This is a nice project but exceptionally easy to do with Erlang and OTP. What you store in structs with &#34;ports&#34; would just be an Erlang process identified by it&#39;s Pid or via a named registered process. These Pids can be monitored by a process so they&#39;re restarted based on your requirements. It would be interesting to see what the comparable system would have looked like with Erlang.</p></pre>samuellampa: <pre><p>Erlang message passing does not block, which is a problem for the &#34;implicit scheduler via a pipeline network&#34; idea, since it does not give you the implicit back-pressure that you get with Go&#39;s channels (because of the fixed size buffers).</p> <p>It is <em>possible</em> to implement Go-like channels etc, (which is done in ElixirFBP afaik: <a href="https://github.com/pcmarks/ElixirFBP">https://github.com/pcmarks/ElixirFBP</a> ), but then you need this extra framework, while in Go you can implement it straight in the language itself, reducing the amount of code a lot.</p></pre>samuellampa: <pre><p>Now, there <em>are</em> scientific workflow management systems implemented in Erlang, of which Cuneiform is an impressive one (<a href="http://www.cuneiform-lang.org/" rel="nofollow">http://www.cuneiform-lang.org/</a>), but AFAIK, it is a pretty different beast altogether, and AFAIS requires to work with the separate execution framework Hi-Way.</p> <p>(Citing from the paper [1]: &#34;Together, Cuneiform and Hi-WAY form a fully functional, scalable, and easily extensible scientific workflow system.&#34;)</p> <p>[1] <a href="http://ceur-ws.org/Vol-1330/paper-03.pdf" rel="nofollow">http://ceur-ws.org/Vol-1330/paper-03.pdf</a></p></pre>cratylus: <pre><p>Ada&#39;s &#34;task entries&#34; do though ..:)</p></pre>Lord_NShYH: <pre><p>This is amazing to me. Thanks!</p></pre>MassiveHyperion: <pre><p>I think you hit the nail on the head. A challenge that needs concurrency, easy in go, hard and messy in most other languages. Anything multi-threaded.</p></pre>xiaodai: <pre><p>U got a specific problem you can share?</p></pre>carleeto: <pre><p>I don&#39;t think the difficulty of the programming challenge is the thing. I think it is the ease with which you can read the code and reason about it. The maintainability. Even using multi-threaded examples, the code is readable and easy to reason about. I think THAT is Go&#39;s strength. </p> <p>I see Go actually being quite boring and simple and that is a good thing. Boring and simple gives you maintainable and readable code, with good design behind it. </p> <p>It conveys what the intention is really well and the disconnect between the intention of the code and the implementation is responsible for far too many bugs IMO.</p></pre>hexmiles: <pre><p>It may be a little off-topic but for me is the ability to work in a team. gofmt make keeping the same style across a team very easy also the convention for testing e benchmarking make building a stable product much more pleasent than other language</p></pre>lazy_jones: <pre><p>Anything that does work concurrently and waits for results:</p> <p><a href="https://talks.golang.org/2012/concurrency.slide#46">https://talks.golang.org/2012/concurrency.slide#46</a></p></pre>Redundancy_: <pre><p>Create a tool that you can easily distribute to users on multiple OSes, which does a large amount of work in the background while running a local web service that shows live status etc.</p></pre>Gacnt: <pre><p>I&#39;m fairly new to Go myself. But I am implementing an interface for dealing with an API from a game server you can see here</p> <p><a href="https://github.com/Gacnt/go-pubg/blob/master/wsconnect.go" rel="nofollow">https://github.com/Gacnt/go-pubg/blob/master/wsconnect.go</a></p> <p>The nice thing I found so far with Go, is I can easily use channels to block the REST request from responding with a result until the channel receives a response from the websocket</p> <p>Open to any criticisms on how to do anything better! </p></pre>tv64738: <pre><p><a href="https://talks.golang.org/2013/oscon-dl.slide" rel="nofollow">https://talks.golang.org/2013/oscon-dl.slide</a> depending on your value of &#34;easy&#34;</p></pre>Gommle: <pre><p>Day 10 of Advent of Code 2016 (the robot chip factory) has a really interesting solution using Go channels: <a href="https://adventofcode.com/2016/day/10" rel="nofollow">https://adventofcode.com/2016/day/10</a></p> <p>See e.g. <a href="https://github.com/roessland/advent-of-code-2015/blob/master/2016/day-10/part1.go" rel="nofollow">this solution</a>.</p> <p>Instead of a smart solution, this just simulates the whole factory using one goroutine for each robot.</p></pre>nevyn: <pre><p>This is kind of a weird question, because there are a lot of languages and the two &#34;main&#34; things that Go has are easy green threads and channels are in at least some of the others. At which point you need to look at things like the benefits of network effects. More developers means it&#39;s much easier to find a Go programmer than an ocaml/lisp/erlang one (I&#39;d say dito train one too, but flames incoming). Obviously more developers means more available APIs to be able to import.</p> <p>On a similar note one of the surprising things for me, after using C/perl/python/lisp was std. lib. time and how easy it was to do multi timezone things.</p></pre>slowratatoskr: <pre><p>meh, all this golang concurrency primitves looks bad when compared to erlang</p></pre>diosio: <pre><p>Isn&#39;t Erlang somewhat slower than go tho? </p></pre>slowratatoskr: <pre><p>yeah, but assuming that you&#39;re doing networking related apps, its <a href="https://m.youtube.com/watch?v=uKfKtXYLG78" rel="nofollow">great</a></p></pre>diosio: <pre><p>A guy I used to work with used erlang for a highly-concurrent/available application, but said they moved to C as erlang was being somewhat slow. Although they did sing praises about the language and its fault tolerance!</p></pre>classhero: <pre><p>Testing concurrency edge cases. Very readable in Go because it&#39;s so easy to fire off some coroutines vs dealing with a bunch of thread boilerplate (particularly worse in languages that don&#39;t even have lambdas)</p></pre>sh41: <pre><p><strong>Edit:</strong> I agree with the replies, this is actually not a great example. You can in many other languages.</p> <p><del>I like the Fibonacci example.</del></p> <pre><code>package main import &#34;fmt&#34; // fib returns a function that returns // successive Fibonacci numbers. func fib() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } } func main() { f := fib() fmt.Println(f(), f(), f(), f(), f()) // Output: 1 1 2 3 5 } </code></pre></pre>mister_plinkett: <pre><p>You can do basically the same thing in plenty of other languages. Even the mutli-assignment of <code>a, b := 0, 1</code> isn&#39;t Go specific, take this python for example:</p> <pre><code>def fib(): a, b = 0, 1 while True: a, b = b, a+b yield a </code></pre></pre>roxven: <pre><p>I think this was a poor example considering that the lazy Fibonacci sequence has been used to brag about Haskell for decades:</p> <pre><code> fibs = 0 : 1 : zipWith (+) fibs (tail fibs) fifthFib = fibs!!5 </code></pre></pre>w2qw: <pre><p>Any language with closures can do that.</p></pre>one_zer: <pre><p>95% of the way there without closures...</p> <pre><code>#include&lt;stdio.h&gt; long fib() { static long a=0,b=1,tmp; return tmp=a+b, a=b, b=tmp; } int main(int argc, char *argv[]) { for(int i=0;i&lt;10;i++) printf(&#34;%ld\n&#34;,fib()); return 0; } </code></pre></pre>censored_username: <pre><p>That fails if you ever need more than 1 fibonacci instance though. That&#39;s the nice thing about closures.</p> <p>And adding another impl here for the record:</p> <pre><code>fn main() { let mut f = fib(); println!(&#34;{} {} {} {} {}&#34;, f(), f(), f(), f(), f()); } fn fib() -&gt; Box&lt;FnMut() -&gt; usize&gt; { let mut a = (0, 1); Box::new(move ||{ a = (a.1, a.0 + a.1); a.0 }) } </code></pre></pre>_ak: <pre><p>A more Go-specific solution would probably be the goroutine/channel-based one as presented in the Go tour: <a href="https://tour.golang.org/concurrency/5" rel="nofollow">https://tour.golang.org/concurrency/5</a></p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

472 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传