Why golang refuse to provide goroutine id, then they hack their own way to do it?

agolangf · · 500 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>This is aweful: <a href="https://github.com/golang/net/blob/master/http2/gotrack.go#L51">https://github.com/golang/net/blob/master/http2/gotrack.go#L51</a></p> <p>Ary they enjoying this?</p> <hr/>**评论:**<br/><br/>kodablah: <pre><p>A madlib: Why golang refuse to provide &lt;feature&gt;, but get to have it for themselves?</p> <p>&lt;feature&gt; can be type-specific containers, goroutine IDs, etc. It comes down to exposure caution. I don&#39;t agree with it, but the language maintainers think if it can&#39;t be done right or there are large enough caveats, you can&#39;t be trusted with the capability. They also believe that exposure is tacit usage encouragement and implies compatibility constraints. I prefer feature-gating and experimental features behind a flag if they have benefits, but in their view this is a slippery slope based on previous experiences with other languages (IIRC).</p></pre>F41LUR3: <pre><p>Not sure why you were downvoted for this, but you&#39;re spot on.</p></pre>leimy: <pre><p>Interface exposure can ruin things. If they decide to keep things unstable or at least something they may break later, letting us use it and then breaking us later is harmful. So I get why they do it.</p> <p>Funny though. When you try to program straight to the kernel syscall layer, skipping the C library on Mac OS you are asking for the same kind of trouble. Apple’s stable support seems to end at libSystem. While I don’t wish ill on anyone trying to do what Go’s runtime does - I will guess this is a fragile arrangement at best. Seems to have been going ok so far...</p></pre>bearcherian: <pre><p><a href="https://blog.sgmansfield.com/2015/12/goroutine-ids/">https://blog.sgmansfield.com/2015/12/goroutine-ids/</a></p></pre>nevyn: <pre><p>tl;dr Uses runtime.Stack() to get a stack trace into a buffer and then parse out the goroutine number.</p> <p>It&#39;s all used behind a DEBUG check, but still.</p></pre>pony279: <pre><p>Just can&#39;t understand why golang is so obsessed on preventing people using it. At least, it&#39;s really useful for debugging.</p></pre>dlsspy: <pre><p>I&#39;ve been writing servers in go since the day it went public. I&#39;ve written several production systems and concurrency toolkits. It&#39;s never been a thing I felt I needed.</p> <p>Similarly, someone came into #haskell-beginners recently asking how we log from within functions without turning them all into io actions (where side effects are allowed). We couldn&#39;t get very concrete because the person with the question didn&#39;t have a problem, but a strategy for solving problems in other languages he felt he&#39;d be lost without in Haskell.</p> <p>If you do things idiomatically, you struggle a lot less.</p></pre>itsmontoya: <pre><p>This is it for me. I&#39;ve never encountered a situation where this would be needed.</p></pre>jrwren: <pre><p>same as generics. same as macros. my favorite summary is <a href="http://nomad.so/2015/03/why-gos-design-is-a-disservice-to-intelligent-programmers/" rel="nofollow">http://nomad.so/2015/03/why-gos-design-is-a-disservice-to-intelligent-programmers/</a></p></pre>shovelpost: <pre><p>Yeah, except the author of that article now uses Go. ;D</p></pre>jrwren: <pre><p>I do too. </p></pre>ngrilly: <pre><p>Yes:</p> <blockquote> <p>I’ve written before about Go and how I was initially pretty unimpressed with the over simplicity and parenting that is forced upon you. However, over time I’ve come to enjoy using Go to the point that every new project I tackle, Go is the first language I consider.</p> </blockquote> <p>(source: <a href="http://nomad.so/2017/07/go-v2-0-and-generics-its-a-no-brainer/" rel="nofollow">http://nomad.so/2017/07/go-v2-0-and-generics-its-a-no-brainer/</a>)</p> <p>The main thing missing are generics / type parametricity according to the author, and I tend to agree.</p></pre>shovelpost: <pre><p>It&#39;s right at the top.</p> <blockquote> <p>// Defensive debug-only utility to track that functions run on the</p> <p>// goroutine that they&#39;re supposed to.</p> </blockquote></pre>pony279: <pre><p>Does that means go-coders never need goroutine id for debugging except the go team themself?</p></pre>shovelpost: <pre><p>If you really feel that you need for debugging purposes then do as Brad did.</p></pre>dlsniper: <pre><p>Yes. In more than 4 years of working with Go un a professional environment I&#39;ve never needed the goroutine id. This is because I also never worked in a programming language which would expose that so I always had a different solution than others who did. </p> <p>It&#39;s a matter of just thinking about the problem and not trying to shoehorn a solution you are familiar with. That&#39;s the biggest problem all of the Go newbie programmers have. </p></pre>bkeroack: <pre><p>I don&#39;t get the anger. Apparently you feel like you &#34;caught&#34; the Go team doing something.</p> <p>If you have one of the rare use cases for goroutine ID or TLS, it&#39;s obviously possible to implement, as they did. If you have a similar need, go right ahead.</p> <p>However clearly the Go team feels it&#39;s often a bad idea and leads to poor design choices, so they don&#39;t feel any need to make it convenient or advertise its use.</p> <p>What&#39;s the problem?</p></pre>pony279: <pre><p>Its because go is the best choice both me and my coleages write that fits the job. I really didnt like it after a few projects. I’ve switched to other language but sometimes I still need ‘go’ back to get the shit down. I felt that after I get rid of it I will not need to care about what the go team forces their users to do any more.</p> <p>They are not wrong at designing it. Maybe its just because the tool doesnt fit me.</p></pre>snippet2: <pre><p>How were you able to set this up in another language?</p></pre>pony279: <pre><p>It’s analogy.</p> <p>Depending on how your system runs. With multi threading, you use thread id to build thread local storage. With multi processes, just use global variable (process local) when you need it.</p> <p>I just wonder why go didn&#39;t ban global variable because it&#39;s easy to abuse too.</p></pre>snippet2: <pre><p>Almost every language has them and idk why. I guess those really rare use cases. But everyone knows not to use globals. That&#39;s like programming 101. I think they didn&#39;t use goroutine ids cause it would turn into a mess the bigger the project. That&#39;s kinda the point of go.</p></pre>pony279: <pre><blockquote> <p>But everyone knows not to use globals.</p> </blockquote> <p>This is what we&#39;ve been told to.</p> <p>But global variable is actually quite common. It&#39;s not that bad. Really. I can find globals in many open source projects.</p> <p>We use it when we really need it. Otherwise a big context needs to be passed around, and it doesn&#39;t make much difference except for makeing the code much longer.</p> <blockquote> <p>I think they didn&#39;t use goroutine ids cause it would turn into a mess the bigger the project. That&#39;s kinda the point of go.</p> </blockquote> <p>IMO this is not a valid point.</p> <p>Thread/goroutine local is the same thing as global var by analogy. It&#39;s just global for thread/goroutine. </p> <p>It turns into a mess only when the developers don&#39;t understand the architecture of the project they&#39;re working on, and there will be more serious mess no matter whether they use it or not.</p></pre>snippet2: <pre><p>Well if the project never grows, no one else touches it, neither happens now until forever, then you have a totally valid point for only your use case. But I believe you are trying to be witty. </p></pre>65a: <pre><p>Non-sarcastic question, why do you need this?</p></pre>jerf: <pre><p>I have a use case where I&#39;d like to gob.Decode a struct that contains things that implement encoding.BinaryMarshaler, and I have some context that I&#39;d like to pass into the BinaryMarshaler. There is no way to do that. So instead I wrap a lock around the entire function, and when a user takes the lock, they also set the context into a global variable shielded by that variable, and the unmarshal code can then find what they need. Fortunately, for various reasons, I don&#39;t really need multiple cores running this at once as there is a bottleneck elsewhere that prevents that. But it would still be cleaner if I could pass contexts into functions that implement interfaces that don&#39;t currently take contexts.</p> <p>(In this case, I don&#39;t strictly speaking mean the Context package&#39;s Context, though that would be a fairly good option, as that would also allow the <code>go</code> keyword to cleanly and cheaply copy the current goroutine&#39;s Context into the new one.)</p> <p>I actually don&#39;t need the goroutine ID per se; I just need some mechanism to attach a thing to the goroutine itself and then get it back out.</p></pre>kostix: <pre><p>A way to attach something to a goroutine is to turn it into a variable inside the function running on that goroutine.</p> <p>This &#34;something&#34; can be passed to the goroutine via the function&#39;s formal parameters or, say, sent to it via a channel.</p> <p>To may be make this more clear, a live goroutine references all the variables the function running on it has access to—that is, allocated itself or closed over at its creation time.</p></pre>jerf: <pre><p>No, it can&#39;t. I can&#39;t control how gob.Decode works, nor can I control the parameters that <a href="https://golang.org/pkg/encoding/#BinaryMarshaler">UnmarshalBinary</a> takes, which is the function that needs this context.</p> <p>There is no closure path to pass this in, because gob.Decode directly uses reflect to create objects; gob.Decode doesn&#39;t have a <code>func(i *interface{})</code> or something that gets run after an object is created to allow you to introspect and modify it or something. There is no path to get from the code invoking gob.Decode that I can see.</p> <p>If I am wrong, I&#39;d love to know. Your task, should you choose to accept it, is to fix up <a href="https://play.golang.org/p/JLrN02OC9c">this playground snippet</a> to transmit the <code>x = 5</code> value into the UnmarshalBinary function.</p> <p>Note that in my real code, I am receiving a heterogeneous list of unknown length of interface values. You might be tempted to try to initialize the Outer&#39;s Inner value with the 5 to pass it in. However, along with the fact that doesn&#39;t work (I checked), it also wouldn&#39;t help me in my real code case. That said, if you did find some way to at least do that, I&#39;d still happily give out internet points. The other problem I have is that <em>arbitrary</em> structs containing the type I need to UnmarshalBinary come in; it&#39;s end-user extensible. I have considered a reflect-based solution that recursively walks the incoming data looking for addresses to fix it up after the fact, but this is very likely to be a performance-sensitive bit of the code.</p> <p>I could also require anyone who uses these values to implement a way of presenting them so they could be fixed up after the fact, but that&#39;s going to be fairly inconvenient to the end-user too.</p> <p>In my real code, I&#39;m not trying to cross package boundaries, so if that helps you can use it.</p></pre>vhodges: <pre><p>Perhaps there&#39;s a different way to solve the problem you&#39;re trying to solve but... if you need really the data in that function, I&#39;d probably prepend it to the byte slice and decode that bit first before decoding the rest using the value you prepended.</p> <p>My need for thread id was to use it for thread local storage (to implement something similar to what we do in Ruby). At the end of the day I settled on a package level global map keyed by an application level value (an database id) and it works well enough.</p></pre>Fwippy: <pre><p>If you have context that affects how an object should be marshalled, consider putting that context in the object.</p></pre>pony279: <pre><p><a href="https://github.com/tylerb/gls" rel="nofollow">https://github.com/tylerb/gls</a></p> <p>As mentioned in the readme. I have the same use case.</p></pre>kostix: <pre><p>Looks like you need <a href="https://rakyll.org/profiler-labels/">profiler labels</a>.</p> <p>Even w/o them, you can use a <code>context.Context</code> to carry this information over.</p></pre>andradei: <pre><p>So, could the http2 package make use of this instead of the current hack?</p></pre>pony279: <pre><p>What&#39;s the deference except it requires more code? Does context.Context brings the context explicit? I don&#39;t think so. All it exposes are it&#39;s interface methods, not really making things clear.</p></pre>epiris: <pre><p>Creating your own demand for this feature with that use case, context is a correct way to solve this. That said, a G&#39;s ID isn&#39;t even a good contrived solution, as your correlation is lost if your request passes through another goroutine at any point.</p></pre>watr: <pre><p>As the author clearly states in his readme. This is the lazy way to do it. If you use goroutineID outside of debugging, you will go straight to H(ask)ELL!</p></pre>GoHomeGrandmaUrHigh: <pre><p>Couldn&#39;t he just refactor his code and make his <code>http.Handler</code> functions attached to his app object? i.e.</p> <pre><code>func (a *App) AboutPage(w http.ResponseWriter, r *http.Request) </code></pre> <p>The function signatures of the endpoints can be &#34;standard lib vanilla&#34; and you can stick extra things on that <code>*App</code> object. Or find a similar way to shuffle your functions around to get a &#34;request global context&#34; of some sort that doesn&#39;t involve changing method signatures.</p> <p>edit: by &#34;easy refactor&#34;, if his functions were plain vanilla <code>http.Handlers</code> it&#39;s a copy-paste operation to insert <code>(a *App)</code> before every function. The logic of each function doesn&#39;t need to change because they already were used to <em>not</em> having <code>a</code> available.</p> <p>Or am I missing part of the problem?</p></pre>pony279: <pre><p>Well. Then. Java programmers are all in hell already.</p></pre>Creshal: <pre><p>Of course they are. Why create a new language just to repeat all past mistakes in it?</p></pre>qu33ksilver: <pre><p>Strange way to justify one&#39;s code. Just because somebody else is doing something, does not mean you have to do it too.</p></pre>watr: <pre><p>Java: Serving as an express trip operator to hell for programmers since 1995</p></pre>tetroxid: <pre><p>At least hell has generics and exceptions</p></pre>AllsWellThatsHaskell: <pre><p>but checked exceptions tho...</p></pre>captncraig: <pre><p>Bite the bullet and pass a context. Sorry.</p></pre>pony279: <pre><p>Don&#39;t be sorry</p></pre>cenuij: <pre><p>It will be abused, better to not have it to satisfy a few extreme corner cases.</p></pre>pony279: <pre><p>Global variable (process local) is easy to be abused too, better not to have it,</p></pre>cafxx1985: <pre><p>Can&#39;t agree more. While we&#39;re at it, we should also get rid of mutable state and concurrency because they lead to data races. (no, the race detector doesn&#39;t guarantee race-freedom)</p></pre>cafxx1985: <pre><p>I will venture to guess I&#39;m getting downvoted because people can&#39;t understand whether I&#39;m joking or not. :D</p> <p>(more precisely, people seem to fail to understand that there are good arguments for both positions implied in my post... hence I guess the only senseless thing to do is downvoting)</p></pre>nsd433: <pre><p>Such a function used to exist in early Go. It was removed because people started using it to implement goroutine-local-storage by using a global map keyed with the goroutine ID. That&#39;s not good because it beaks in the future if/when callbacks are called in a different/new goroutine. In other words it makes which goroutine calls the callback part of every API, which isn&#39;t always desirable.</p> <p>(The proper way of passing data to a callback is a closure. That will always work, no matter which goroutine calls the callback.)</p> <p>That said I personally find goroutine IDs to be very very useful in a log or trace file. So I use the source and patch a runtime.GoID() function back into my runtime. The code is trivial</p> <pre><code>func GoID() int64 { return getg().goid } </code></pre> <p>If you also patch in a matching build flag into the compiler then you can produce source code which compiles with and without the patched runtime.</p></pre>hobbified: <pre><p>How drunk are you?</p></pre>

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

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