This commit is contained in:
Teiva Harsanyi 2024-10-09 13:38:14 +02:00
parent 8d85d6cdef
commit a6c4ae916d
21 changed files with 266 additions and 259 deletions

View file

@ -1,5 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block announce %} {% block announce %}
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
{% endblock %} {% endblock %}

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -91,7 +91,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>
@ -1190,7 +1190,7 @@
<li><code>io.Writer</code> writes data to a target</li> <li><code>io.Writer</code> writes data to a target</li>
</ul> </ul>
<p>What is the rationale for having these two interfaces in the language? What is the point of creating these abstractions?</p> <p>What is the rationale for having these two interfaces in the language? What is the point of creating these abstractions?</p>
<p>Lets assume we need to implement a function that should copy the content of one file to another. We could create a specific function that would take as input two <code>*os.Files</code>. Or, we can choose to create a more generic function using <code>io.Reader</code> and <code>io.Writer</code> abstractions:</p> <p>Lets assume we need to implement a function that should copy the content of one file to another. We could create a specific function that would take as input two <code>*os.File</code>. Or, we can choose to create a more generic function using <code>io.Reader</code> and <code>io.Writer</code> abstractions:</p>
<div class="language-go highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="kd">func</span><span class="w"> </span><span class="nx">copySourceToDest</span><span class="p">(</span><span class="nx">source</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span><span class="w"> </span><span class="nx">dest</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Writer</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span> <div class="language-go highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="kd">func</span><span class="w"> </span><span class="nx">copySourceToDest</span><span class="p">(</span><span class="nx">source</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">,</span><span class="w"> </span><span class="nx">dest</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Writer</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="w"> </span><span class="c1">// ...</span> </span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="w"> </span><span class="c1">// ...</span>
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="p">}</span> </span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="p">}</span>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -135,7 +135,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>
@ -5087,7 +5087,7 @@ the use case. However, we should see the two options as complementary. </p>
</span><span id="__span-70-10"><a id="__codelineno-70-10" name="__codelineno-70-10" href="#__codelineno-70-10"></a><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span> </span><span id="__span-70-10"><a id="__codelineno-70-10" name="__codelineno-70-10" href="#__codelineno-70-10"></a><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
</span><span id="__span-70-11"><a id="__codelineno-70-11" name="__codelineno-70-11" href="#__codelineno-70-11"></a><span class="p">}</span> </span><span id="__span-70-11"><a id="__codelineno-70-11" name="__codelineno-70-11" href="#__codelineno-70-11"></a><span class="p">}</span>
</span></code></pre></div> </span></code></pre></div>
<p>Runnig this code with the <code>-race</code> logs the following warning:</p> <p>Running this code with the <code>-race</code> logs the following warning:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-71-1"><a id="__codelineno-71-1" name="__codelineno-71-1" href="#__codelineno-71-1"></a><span class="o">==================</span> <div class="language-bash highlight"><pre><span></span><code><span id="__span-71-1"><a id="__codelineno-71-1" name="__codelineno-71-1" href="#__codelineno-71-1"></a><span class="o">==================</span>
</span><span id="__span-71-2"><a id="__codelineno-71-2" name="__codelineno-71-2" href="#__codelineno-71-2"></a>WARNING:<span class="w"> </span>DATA<span class="w"> </span>RACE </span><span id="__span-71-2"><a id="__codelineno-71-2" name="__codelineno-71-2" href="#__codelineno-71-2"></a>WARNING:<span class="w"> </span>DATA<span class="w"> </span>RACE
</span><span id="__span-71-3"><a id="__codelineno-71-3" name="__codelineno-71-3" href="#__codelineno-71-3"></a><span class="hll">Write<span class="w"> </span>at<span class="w"> </span>0x00c000026078<span class="w"> </span>by<span class="w"> </span>goroutine<span class="w"> </span><span class="m">7</span>:<span class="w"> </span><span class="c1"># (1)</span> </span><span id="__span-71-3"><a id="__codelineno-71-3" name="__codelineno-71-3" href="#__codelineno-71-3"></a><span class="hll">Write<span class="w"> </span>at<span class="w"> </span>0x00c000026078<span class="w"> </span>by<span class="w"> </span>goroutine<span class="w"> </span><span class="m">7</span>:<span class="w"> </span><span class="c1"># (1)</span>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

View file

@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>

File diff suppressed because one or more lines are too long

View file

@ -2,77 +2,77 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url> <url>
<loc>https://100go.co/</loc> <loc>https://100go.co/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/20-slice/</loc> <loc>https://100go.co/20-slice/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/28-maps-memory-leaks/</loc> <loc>https://100go.co/28-maps-memory-leaks/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/5-interface-pollution/</loc> <loc>https://100go.co/5-interface-pollution/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/56-concurrency-faster/</loc> <loc>https://100go.co/56-concurrency-faster/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/89-benchmarks/</loc> <loc>https://100go.co/89-benchmarks/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/9-generics/</loc> <loc>https://100go.co/9-generics/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/92-false-sharing/</loc> <loc>https://100go.co/92-false-sharing/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/98-profiling-execution-tracing/</loc> <loc>https://100go.co/98-profiling-execution-tracing/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/book/</loc> <loc>https://100go.co/book/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/chapter-1/</loc> <loc>https://100go.co/chapter-1/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/external/</loc> <loc>https://100go.co/external/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/ja/</loc> <loc>https://100go.co/ja/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/pt-br/</loc> <loc>https://100go.co/pt-br/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
<url> <url>
<loc>https://100go.co/zh/</loc> <loc>https://100go.co/zh/</loc>
<lastmod>2024-10-06</lastmod> <lastmod>2024-10-09</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
</url> </url>
</urlset> </urlset>

Binary file not shown.

View file

@ -6,3 +6,10 @@
.md-typeset details { .md-typeset details {
font-size: 15px font-size: 15px
} }
.md-announce {
background-color: #f0f8ff; /* Light blue background */
padding: 10px;
border-radius: 5px;
color: #333;
}

View file

@ -121,7 +121,7 @@
<div data-md-component="skip"> <div data-md-component="skip">
<a href="#100go" class="md-skip"> <a href="#100-go" class="md-skip">
Skip to content Skip to content
</a> </a>
@ -137,7 +137,7 @@
</button> </button>
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>. 📢 I have just released a daily newsletter for coders: The Coder Cafe ☕. Feel free to have a look at <a href="https://thecoder.cafe">thecoder.cafe</a>.
</div> </div>
@ -1025,7 +1025,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#nilslice-22" class="md-nav__link"> <a href="#nil-slice-22" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
困惑于 nil 和空 slice (#22) 困惑于 nil 和空 slice (#22)
</span> </span>
@ -1130,9 +1130,9 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#range-range-loops-32" class="md-nav__link"> <a href="#range-32" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
忽略了 range 循环中指针元素的影响 range loops (#32) 忽略了 range 循环中指针元素的影响 (#32)
</span> </span>
</a> </a>
@ -1357,7 +1357,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#52" class="md-nav__link"> <a href="#52" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
处理同一个错误两次 (#52) 两次处理同一个错误 (#52)
</span> </span>
</a> </a>
@ -1415,7 +1415,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#channelsmutexes-57" class="md-nav__link"> <a href="#channels-mutexes-57" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不清楚何时使用 channels 或 mutexes (#57) 不清楚何时使用 channels 或 mutexes (#57)
</span> </span>
@ -1645,7 +1645,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#http-sqlrows-osfile-79" class="md-nav__link"> <a href="#http-sqlrows-osfile-79" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不关闭临时资源HTTP 请求体、sql.Rows 和 os.File) (#79) 不关闭临时资源HTTP 请求体、sql.Rows 和 os.File (#79)
</span> </span>
</a> </a>
@ -1661,7 +1661,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#http-clientserver-81" class="md-nav__link"> <a href="#http-client-server-81" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
直接使用默认的 HTTP client 和 server (#81) 直接使用默认的 HTTP client 和 server (#81)
</span> </span>
@ -1835,9 +1835,9 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#api-compiler-optimizations-and-syncpool-96" class="md-nav__link"> <a href="#api-syncpool-96" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不知道如何减少内存分配次数 (API调整, compiler optimizations, and sync.Pool) (#96) 不知道如何减少内存分配次数API 调整,编译器优化和 sync.Pool (#96)
</span> </span>
</a> </a>
@ -1871,7 +1871,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#dockerk8sgo-100" class="md-nav__link"> <a href="#docker-k8s-go-100" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不了解 Docker 或者 K8S 对运行的 Go 应用的性能影响 (#100) 不了解 Docker 或者 K8S 对运行的 Go 应用的性能影响 (#100)
</span> </span>
@ -2303,7 +2303,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#nilslice-22" class="md-nav__link"> <a href="#nil-slice-22" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
困惑于 nil 和空 slice (#22) 困惑于 nil 和空 slice (#22)
</span> </span>
@ -2408,9 +2408,9 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#range-range-loops-32" class="md-nav__link"> <a href="#range-32" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
忽略了 range 循环中指针元素的影响 range loops (#32) 忽略了 range 循环中指针元素的影响 (#32)
</span> </span>
</a> </a>
@ -2635,7 +2635,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#52" class="md-nav__link"> <a href="#52" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
处理同一个错误两次 (#52) 两次处理同一个错误 (#52)
</span> </span>
</a> </a>
@ -2693,7 +2693,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#channelsmutexes-57" class="md-nav__link"> <a href="#channels-mutexes-57" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不清楚何时使用 channels 或 mutexes (#57) 不清楚何时使用 channels 或 mutexes (#57)
</span> </span>
@ -2923,7 +2923,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#http-sqlrows-osfile-79" class="md-nav__link"> <a href="#http-sqlrows-osfile-79" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不关闭临时资源HTTP 请求体、sql.Rows 和 os.File) (#79) 不关闭临时资源HTTP 请求体、sql.Rows 和 os.File (#79)
</span> </span>
</a> </a>
@ -2939,7 +2939,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#http-clientserver-81" class="md-nav__link"> <a href="#http-client-server-81" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
直接使用默认的 HTTP client 和 server (#81) 直接使用默认的 HTTP client 和 server (#81)
</span> </span>
@ -3113,9 +3113,9 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#api-compiler-optimizations-and-syncpool-96" class="md-nav__link"> <a href="#api-syncpool-96" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不知道如何减少内存分配次数 (API调整, compiler optimizations, and sync.Pool) (#96) 不知道如何减少内存分配次数API 调整,编译器优化和 sync.Pool (#96)
</span> </span>
</a> </a>
@ -3149,7 +3149,7 @@
</li> </li>
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#dockerk8sgo-100" class="md-nav__link"> <a href="#docker-k8s-go-100" class="md-nav__link">
<span class="md-ellipsis"> <span class="md-ellipsis">
不了解 Docker 或者 K8S 对运行的 Go 应用的性能影响 (#100) 不了解 Docker 或者 K8S 对运行的 Go 应用的性能影响 (#100)
</span> </span>
@ -3180,7 +3180,7 @@
<h1 id="100go">100个Go常见错误及如何避免</h1> <h1 id="100-go">100 个 Go 常见错误及如何避免</h1>
<details class="tip" open="open"> <details class="tip" open="open">
<summary>Jobs</summary> <summary>Jobs</summary>
<p>Is your company hiring? <a href="https://github.com/sponsors/teivah/sponsorships?sponsor=teivah&amp;tier_id=386213&amp;preview=true">Sponsor</a> the Chinese version of this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.</p> <p>Is your company hiring? <a href="https://github.com/sponsors/teivah/sponsorships?sponsor=teivah&amp;tier_id=386213&amp;preview=true">Sponsor</a> the Chinese version of this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.</p>
@ -3206,19 +3206,19 @@
<h3 id="9"><a href="https://100go.co/9-generics/">困惑何时该用范型</a> (#9)</h3> <h3 id="9"><a href="https://100go.co/9-generics/">困惑何时该用范型</a> (#9)</h3>
<p>使用泛型,可以通过类型参数分离具体的数据类型和行为,避免写很多重复度很高的代码。然而,不要过早地使用泛型、类型参数,只有在你看到真正需要时才使用。否则,它们会引入不必要的抽象和复杂性。</p> <p>使用泛型,可以通过类型参数分离具体的数据类型和行为,避免写很多重复度很高的代码。然而,不要过早地使用泛型、类型参数,只有在你看到真正需要时才使用。否则,它们会引入不必要的抽象和复杂性。</p>
<h3 id="10">未意识到类型嵌套的可能问题 (#10)</h3> <h3 id="10">未意识到类型嵌套的可能问题 (#10)</h3>
<p>使用类型嵌套也可以避免写一些重复代码,然而,在使用时需要确保不会导致不合理的可见性问题,比如有些字段应该对外隐藏不应该被暴</p> <p>使用类型嵌套也可以避免写一些重复代码,然而,在使用时需要确保不会导致不合理的可见性问题,比如有些字段应该对外隐藏不应该被暴</p>
<h3 id="function-option-11">不使用 function option 模式 (#11)</h3> <h3 id="function-option-11">不使用 function option 模式 (#11)</h3>
<p>为了设计并提供更友好的 API可选参数为了更好地处理选项应该使用 function option 模式。</p> <p>为了设计并提供更友好的 API可选参数为了更好地处理选项应该使用 function option 模式。</p>
<h3 id="12">工程组织不合理 (工程结构和包的组织) (#12)</h3> <h3 id="12">工程组织不合理 (工程结构和包的组织) (#12)</h3>
<p>遵循像 project-layout 的建议来组织Go工程是一个不错的方法特别是你正在寻找一些类似的经验、惯例来组织一个新的Go工程的时候。</p> <p>遵循像 project-layout 的建议来组织 Go 工程是一个不错的方法,尤其是你正在寻找一些类似的经验、惯例来组织一个新的 Go 工程的时候。</p>
<h3 id="13">创建工具包 (#13)</h3> <h3 id="13">创建工具包 (#13)</h3>
<p>命名是软件设计开发中非常重要的一个部分,创建一些名如 <code>common</code><code>util</code><code>shared</code> 之类的包名并不会给读者带来太大价值,应该将这些包名重构为更清晰、更聚焦的包名。</p> <p>命名是软件设计开发中非常重要的一个部分,创建一些名如 <code>common</code><code>util</code><code>shared</code> 之类的包名并不会给读者带来太大价值,应该将这些包名重构为更清晰、更具体的包名。</p>
<h3 id="14">忽略了包名冲突 (#14)</h3> <h3 id="14">忽略了包名冲突 (#14)</h3>
<p>为了避免变量名和包名之间的冲突,导致混淆或甚至错误,应为每个变量和包使用唯一的名称。如果这不可行,可以考虑使用导入别名 <code>import importAlias 'importPath'</code>以区分包名和变量名,或者考虑一个更好的变量名。</p> <p>为了避免变量名和包名之间的冲突,导致混淆或甚至错误,应为每个变量和包使用唯一的名称。如果这不可行,可以考虑使用导入别名 <code>import importAlias 'importPath'</code> 以区分包名和变量名,或者考虑一个更好的变量名。</p>
<h3 id="15">代码缺少文档 (#15)</h3> <h3 id="15">代码缺少文档 (#15)</h3>
<p>为了让使用方、维护人员能更清晰地了解你的代码的意图,导出的元素(函数、类型、字段)需要添加godoc注释。</p> <p>为了让使用方、维护人员能更清晰地了解你的代码的意图,导出的元素(函数、类型、字段)需要添加注释。</p>
<h3 id="linters-16">不使用 linters 检查 (#16)</h3> <h3 id="linters-16">不使用 linters 检查 (#16)</h3>
<p>为了改善代码质量、整体代码的一致性,应该使用linters、formatters。</p> <p>为了改善代码质量、整体代码的一致性,应该使用 linters 和 formatters。</p>
<h2 id="_2">数据类型</h2> <h2 id="_2">数据类型</h2>
<h3 id="17">八进制字面量引发的困惑 (#17)</h3> <h3 id="17">八进制字面量引发的困惑 (#17)</h3>
<p>在阅读现有代码时,请记住以 0 开头的整数字面量是八进制数。此外,为了提高可读性,可以通过在前面加上 0o 来显式地表示八进制整数。</p> <p>在阅读现有代码时,请记住以 0 开头的整数字面量是八进制数。此外,为了提高可读性,可以通过在前面加上 0o 来显式地表示八进制整数。</p>
@ -3230,34 +3230,34 @@
<h3 id="slice-20"><a href="https://100go.co/20-slice/">不理解 slice 的长度和容量</a> (#20)</h3> <h3 id="slice-20"><a href="https://100go.co/20-slice/">不理解 slice 的长度和容量</a> (#20)</h3>
<p>理解 slice 的长度和容量的区别,是一个 Go 开发者的核心知识点之一。slice 的长度指的是 slice 已经存储的元素的数量,而容量指的是 slice 当前底层开辟的数组最多能容纳的元素的数量。</p> <p>理解 slice 的长度和容量的区别,是一个 Go 开发者的核心知识点之一。slice 的长度指的是 slice 已经存储的元素的数量,而容量指的是 slice 当前底层开辟的数组最多能容纳的元素的数量。</p>
<h3 id="slice-21">不高效的 slice 初始化 (#21)</h3> <h3 id="slice-21">不高效的 slice 初始化 (#21)</h3>
<p>当创建一个slice时如果其长度可以预先确定那么可以在定义时指定它的长度和容量。这可以改善后期append时一次或者多次的内存分配操作从而改善性能。对于map的初始化也是这样的</p> <p>当创建一个 slice 时,如果其长度可以预先确定,那么可以在定义时指定它的长度和容量。这可以改善后期 append 时一次或者多次的内存分配操作,从而改善性能。对于 map 的初始化也是如此</p>
<h3 id="nilslice-22">困惑于nil和空slice (#22)</h3> <h3 id="nil-slice-22">困惑于 nil 和空 slice (#22)</h3>
<p>为了避免常见的对 nil 和 empty slice 处理行为的混淆,例如在使用 encoding/json 或 reflect 包时,你需要理解 nil 和 empty slice 的区别。两者都是长度为零、容量为零的切片,但是 nil 切片不需要分配内存。</p> <p>为了避免常见的对 nil 和 empty slice 处理行为的混淆,例如在使用 encoding/json 或 reflect 包时,你需要理解 nil 和 empty slice 的区别。两者都是长度为零、容量为零的切片,但是 nil 切片不需要分配内存。</p>
<h3 id="slice-23">没有适当检查 slice 是否为空 (#23)</h3> <h3 id="slice-23">没有适当检查 slice 是否为空 (#23)</h3>
<p>检查一个slice的是否包含任何元素可以检查其长度不管slice是nil还是empty检查长度都是有效的。这个检查方法也适用于map。</p> <p>检查一个 slice 是否包含任何元素,可以检查其长度,不管 slice 是 nil 还是 empty检查长度都是有效的。这个检查方法也适用于 map。</p>
<p>为了设计更明确的 APIAPI 不应区分 nil 和空切片。</p> <p>为了设计更明确的 APIAPI 不应区分 nil 和空切片。</p>
<h3 id="slice-24">没有正确拷贝 slice (#24)</h3> <h3 id="slice-24">没有正确拷贝 slice (#24)</h3>
<p>使用 <code>copy</code> 拷贝一个 slice 元素到另一个 slice 时,需要记得,实际拷贝的元素数量是二者 slice 长度中的较小值。</p> <p>使用 <code>copy</code> 拷贝一个 slice 元素到另一个 slice 时,需要记得,实际拷贝的元素数量是二者 slice 长度中的较小值。</p>
<h3 id="slice-append-25">slice append 带来的预期之外的副作用 (#25)</h3> <h3 id="slice-append-25">slice append 带来的预期之外的副作用 (#25)</h3>
<p>如果两个不同的函数操作的slice复用了相同的底层数组它们对slice执行append操作时可能会产生冲突。使用copy来完整复制一个slice或者使用完整的slice表达式[low:high:max]限制最大容量有助于避免产生冲突。当想对一个大slice进行shrink操作时两种方式中只有copy才可以避免内存泄漏。</p> <p>如果两个不同的函数操作的 slice 复用了相同的底层数组,它们对 slice 执行 append 操作时可能会产生冲突。使用 copy 来完整复制一个 slice 或者使用完整的 slice 表达式 <code>[low:high:max]</code> 限制最大容量,有助于避免产生冲突。当想对一个大 slice 进行 shrink 操作时,两种方式中,只有 copy 才可以避免内存泄漏。</p>
<h3 id="slice-26">slice 和内存泄漏 (#26)</h3> <h3 id="slice-26">slice 和内存泄漏 (#26)</h3>
<p>对于slice元素为指针或者slice元素为struct但是该struct含有指针字段当通过slice[low:high]操作取subslice时对于那些不可访问的元素可以显示设置为nil来避免内存泄露。</p> <p>对于 slice 元素为指针,或者 slice 元素为 struct 但是该 struct 含有指针字段,当通过 <code>slice[low:high]</code> 操作取 subslice 时,对于那些不可访问的元素可以显式设置为 nil 来避免内存泄露。</p>
<h3 id="map-27">不高效的 map 初始化 (#27)</h3> <h3 id="map-27">不高效的 map 初始化 (#27)</h3>
<p><a href="#inefficient-slice-initialization-21">#21</a>.</p> <p><a href="#inefficient-slice-initialization-21">#21</a>.</p>
<h3 id="map-28"><a href="https://100go.co/28-maps-memory-leaks/">map 和内存泄漏</a> (#28)</h3> <h3 id="map-28"><a href="https://100go.co/28-maps-memory-leaks/">map 和内存泄漏</a> (#28)</h3>
<p>一个map的buckets占用的内存只会增长,不会缩减。因此,如果它导致了一些内存占用的问题,你需要尝试不同的选项来解决比如重新创建一个map代替原来的原来的map会被GC掉或者map[keyType]valueType中的valueType使用指针代替长度固定的数组或者sliceHeader来缓解过多的内存占用。</p> <p>一个 map 的 buckets 占用的内存只会增长,不会缩减。因此,如果它导致了一些内存占用的问题,你需要尝试不同的方式来解决,比如重新创建一个 map 代替原来的(原来的 map 会被 <abbr title="Garbage Collector">GC</abbr> 掉),或者 <code>map[keyType]valueType</code> 中的 valueType 使用指针代替长度固定的数组或者 sliceHeader 来缓解过多的内存占用。</p>
<h3 id="29">不正确的值比较 (#29)</h3> <h3 id="29">不正确的值比较 (#29)</h3>
<p>Go 中比较两个类型值时,如果是可比较类型,那么可以使用 <code>==</code> 或者 <code>!=</code> 运算符进行比较比如booleans、numerals、strings、pointers、channels以及字段全部是可比较类型的 structs。其他情况下你可以使用 <code>reflect.DeepEqual</code> 来比较,用反射的话会牺牲一点性能,也可以使用自定义的实现和其他库来完成。</p> <p>Go 中比较两个类型值时,如果是可比较类型,那么可以使用 <code>==</code> 或者 <code>!=</code> 运算符进行比较比如booleans、numerals、strings、pointers、channels以及字段全部是可比较类型的 structs。其他情况下你可以使用 <code>reflect.DeepEqual</code> 来比较,用反射的话会牺牲一点性能,也可以使用自定义的实现和其他库来完成。</p>
<h2 id="_3">控制结构</h2> <h2 id="_3">控制结构</h2>
<h3 id="range-30">忽略了 <code>range</code> 循环变量是一个拷贝 (#30)</h3> <h3 id="range-30">忽略了 <code>range</code> 循环变量是一个拷贝 (#30)</h3>
<p><code>range</code> 循环中的循环变量是遍历容器中元素值的一个拷贝。因此,如果元素值是一个 struct 并且想在 <code>range</code> 中修改它,可以通过索引值来访问并修改它,或者使用经典的 for 循环+索引值的写法(除非遍历的元素是一个指针)。</p> <p><code>range</code> 循环中的循环变量是遍历容器中元素值的一个拷贝。因此,如果元素值是一个 struct 并且想在 <code>range</code> 中修改它,可以通过索引值来访问并修改它,或者使用经典的 for 循环+索引值的写法(除非遍历的元素是一个指针)。</p>
<h3 id="range-channels-arrays-31">忽略了 <code>range</code> 循环中迭代目标值的计算方式 (channels 和 arrays) (#31)</h3> <h3 id="range-channels-arrays-31">忽略了 <code>range</code> 循环中迭代目标值的计算方式 (channels 和 arrays) (#31)</h3>
<p>传递给 <code>range</code> 操作的迭代目标对应的表达式的值,只会在循环执行前被计算一次,理解这个有助于避免犯一些常见的错误,例如不高效的channel赋值操作、slice迭代操作。</p> <p>传递给 <code>range</code> 操作的迭代目标对应的表达式的值,只会在循环执行前被计算一次,理解这个有助于避免犯一些常见的错误,例如不高效的 channel 赋值操作和 slice 迭代操作。</p>
<h3 id="range-range-loops-32">忽略了 <code>range</code> 循环中指针元素的影响 <code>range</code> loops (#32)</h3> <h3 id="range-32">忽略了 <code>range</code> 循环中指针元素的影响 (#32)</h3>
<p>这里其实强调的是 <code>range</code> 迭代过程中,迭代变量实际上是一个拷贝假设给另外一个容器元素指针类型赋值且需要对迭代变量取地址转换成指针再赋值的话这里潜藏着一个错误就是for循环迭代变量是 per-variable-per-loop 而不是 per-variable-per-iteration。如果是通过局部变量用迭代变量来初始化或者使用索引值来直接引用迭代的元素将有助于避免拷贝指针(迭代变量的地址)之类的bug。</p> <p>这里其实强调的是 <code>range</code> 迭代过程中,迭代变量实际上是一个拷贝假设给另外一个容器元素(指针类型)赋值,且需要对迭代变量取地址转换成指针再赋值的话,这里潜藏着一个错误,就是 for 循环迭代变量是 per-variable-per-loop 而不是 per-variable-per-iteration。如果是通过局部变量用迭代变量来初始化或者使用索引值来直接引用迭代的元素将有助于避免拷贝指针(迭代变量的地址)之类的 bug。</p>
<h3 id="map-33">map 迭代过程中的错误假设(遍历顺序和迭代过程中插入)(#33)</h3> <h3 id="map-33">map 迭代过程中的错误假设(遍历顺序和迭代过程中插入)(#33)</h3>
<p>使用 map 时,为了能得到确定一致的结果,应该记住 Go 中的 map 数据结构: <p>使用 map 时,为了能得到确定一致的结果,应该记住 Go 中的 map 数据结构:
* 不会按照key对data进行排序遍历时不是按key有序的; * 不会按照 key 对 data 进行排序,遍历时 key 不是有序的;
* 遍历时的顺序,也不是按照插入时的顺序; * 遍历时的顺序,也不是按照插入时的顺序;
* 没有一个确定性的遍历顺序,每次遍历顺序是不同的; * 没有一个确定性的遍历顺序,每次遍历顺序是不同的;
* 不能保证迭代过程中新插入的元素,在当前迭代中能够被遍历到;</p> * 不能保证迭代过程中新插入的元素,在当前迭代中能够被遍历到;</p>
@ -3269,7 +3269,7 @@
<h3 id="rune-36">没有理解 rune (#36)</h3> <h3 id="rune-36">没有理解 rune (#36)</h3>
<p>理解 rune 类型对应的是一个 unicode 码点,每一个 unicode 码点其实是一个多字节的序列,不是一个 byte。这应该是 Go 开发者的核心知识点之一,理解了这个有助于更准确地处理字符串。</p> <p>理解 rune 类型对应的是一个 unicode 码点,每一个 unicode 码点其实是一个多字节的序列,不是一个 byte。这应该是 Go 开发者的核心知识点之一,理解了这个有助于更准确地处理字符串。</p>
<h3 id="37">不正确的字符串遍历 (#37)</h3> <h3 id="37">不正确的字符串遍历 (#37)</h3>
<p>使用 <code>range</code> 操作符对一个string进行遍历实际上是对string对应的 <code>[]rune</code> 进行遍历迭代变量中的索引值表示的当前rune对应的 <code>[]byte</code> 在整个 <code>[]byte(string)</code> 中的起始索引。如果要访问string中的某一个rune比如第三个首先要将字符串转换为 <code>[]rune</code> 然后再按索引值访问。</p> <p>使用 <code>range</code> 操作符对一个 string 进行遍历实际上是对 string 对应的 <code>[]rune</code> 进行遍历,迭代变量中的索引值,表示的当前 rune 对应的 <code>[]rune</code> 在整个 <code>[]rune(string)</code> 中的起始索引。如果要访问 string 中的某一个 rune比如第三个首先要将字符串转换为 <code>[]rune</code> 然后再按索引值访问。</p>
<h3 id="trim-38">误用 trim 函数 (#38)</h3> <h3 id="trim-38">误用 trim 函数 (#38)</h3>
<p><code>strings.TrimRight</code>/<code>strings.TrimLeft</code> 移除在字符串尾部或者开头出现的一些 runes函数会指定一个 rune 集合,出现在集合中的 rune 将被从字符串移除。而 <code>strings.TrimSuffix</code>/<code>strings.TrimPrefix</code> 是移除字符串的一个后缀/前缀。</p> <p><code>strings.TrimRight</code>/<code>strings.TrimLeft</code> 移除在字符串尾部或者开头出现的一些 runes函数会指定一个 rune 集合,出现在集合中的 rune 将被从字符串移除。而 <code>strings.TrimSuffix</code>/<code>strings.TrimPrefix</code> 是移除字符串的一个后缀/前缀。</p>
<h3 id="39">不经优化的字符串拼接操作 (#39)</h3> <h3 id="39">不经优化的字符串拼接操作 (#39)</h3>
@ -3277,7 +3277,7 @@
<h3 id="40">无用的字符串转换 (#40)</h3> <h3 id="40">无用的字符串转换 (#40)</h3>
<p><code>bytes</code> 包提供了一些和 <code>strings</code> 包相似的操作,可以帮助避免 []byte/string 之间的转换。</p> <p><code>bytes</code> 包提供了一些和 <code>strings</code> 包相似的操作,可以帮助避免 []byte/string 之间的转换。</p>
<h3 id="41">子字符串和内存泄漏 (#41)</h3> <h3 id="41">子字符串和内存泄漏 (#41)</h3>
<p>使用一个子字符串的拷贝,有助于避免内存泄漏,因为对一个字符串的s[low:high]操作返回的子字符串其使用了和原字符串s相同的底层数组。</p> <p>使用一个子字符串的拷贝,有助于避免内存泄漏,因为对一个字符串的 <code>s[low:high]</code> 操作返回的子字符串,其使用了和原字符串 s 相同的底层数组。</p>
<h2 id="_5">函数和方法</h2> <h2 id="_5">函数和方法</h2>
<h3 id="42">不知道使用哪种接收器类型 (#42)</h3> <h3 id="42">不知道使用哪种接收器类型 (#42)</h3>
<p>对于接收器类型是采用 value 类型还是 pointer 类型,应该取决于下面这几种因素,比如:方法内是否会对它进行修改,它是否包含了一个不能被拷贝的字段,以及它表示的对象有多大。如果有疑问,接收器可以考虑使用 pointer 类型。</p> <p>对于接收器类型是采用 value 类型还是 pointer 类型,应该取决于下面这几种因素,比如:方法内是否会对它进行修改,它是否包含了一个不能被拷贝的字段,以及它表示的对象有多大。如果有疑问,接收器可以考虑使用 pointer 类型。</p>
@ -3287,11 +3287,11 @@
<p><a href="#从不使用命名的返回值-43">#43</a>.</p> <p><a href="#从不使用命名的返回值-43">#43</a>.</p>
<p>使用命名的返回值,因为它已经被初始化了零值,需要注意在某些情况下异常返回时是否需要给它赋予一个不同的值,比如返回值列表定义了一个有名参数 <code>err error</code>,需要注意 <code>return err</code> 时是否正确地对 <code>err</code> 进行了赋值。</p> <p>使用命名的返回值,因为它已经被初始化了零值,需要注意在某些情况下异常返回时是否需要给它赋予一个不同的值,比如返回值列表定义了一个有名参数 <code>err error</code>,需要注意 <code>return err</code> 时是否正确地对 <code>err</code> 进行了赋值。</p>
<h3 id="nil-45">返回一个 nil 接收器 (#45)</h3> <h3 id="nil-45">返回一个 nil 接收器 (#45)</h3>
<p>当返回一个interface参数时需要小心不要返回一个nil指针而是应该显示返回一个nil值。否则可能会发生一些预期外的问题因为调用方会收到一个非nil的值。</p> <p>当返回一个 interface 参数时,需要小心,不要返回一个 nil 指针,而是应该显式返回一个 nil 值。否则,可能会发生一些预期外的问题,因为调用方会收到一个非 nil 的值。</p>
<h3 id="46">使用文件名作为函数入参 (#46)</h3> <h3 id="46">使用文件名作为函数入参 (#46)</h3>
<p>设计函数时使用 <code>io.Reader</code> 类型作为入参,而不是文件名,将有助于改善函数的可复用性、易测试性。</p> <p>设计函数时使用 <code>io.Reader</code> 类型作为入参,而不是文件名,将有助于改善函数的可复用性、易测试性。</p>
<h3 id="defer-value-47">忽略 <code>defer</code> 语句中参数、接收器值的计算方式 (参数值计算, 指针, 和 value 类型接收器) (#47)</h3> <h3 id="defer-value-47">忽略 <code>defer</code> 语句中参数、接收器值的计算方式 (参数值计算, 指针, 和 value 类型接收器) (#47)</h3>
<p>为了避免 <code>defer</code> 语句执行时就立即计算对defer要执行的函数的参数进行计算可以考虑将要执行的函数放到闭包里面然后通过指针传递参数给闭包内函数或者通过闭包捕获外部变量来解决这个问题。</p> <p>为了避免 <code>defer</code> 语句执行时就立即对 defer 要执行的函数的参数进行计算,可以考虑将要执行的函数放到闭包里面,然后通过指针传递参数给闭包内函数(或者通过闭包捕获外部变量),来解决这个问题。</p>
<h2 id="_6">错误管理</h2> <h2 id="_6">错误管理</h2>
<h3 id="panicking-48">Panicking (#48)</h3> <h3 id="panicking-48">Panicking (#48)</h3>
<p>使用 <code>panic</code> 是 Go 中一种处理错误的方式,但是只能在遇到不可恢复的错误时使用,例如:通知开发人员一个强依赖的模块加载失败了。</p> <p>使用 <code>panic</code> 是 Go 中一种处理错误的方式,但是只能在遇到不可恢复的错误时使用,例如:通知开发人员一个强依赖的模块加载失败了。</p>
@ -3302,27 +3302,27 @@
<h3 id="51">不正确的错误对象值比较 (#51)</h3> <h3 id="51">不正确的错误对象值比较 (#51)</h3>
<p><a href="#不正确的错误类型比较-50">#50</a>.</p> <p><a href="#不正确的错误类型比较-50">#50</a>.</p>
<p>为了表达一个预期内的错误,请使用错误值的方式,并通过 <code>==</code> 或者 <code>errors.Is</code> 来比较。而对于意外错误,则应使用特定的错误类型(可以通过 <code>errors.As</code> 来比较)。</p> <p>为了表达一个预期内的错误,请使用错误值的方式,并通过 <code>==</code> 或者 <code>errors.Is</code> 来比较。而对于意外错误,则应使用特定的错误类型(可以通过 <code>errors.As</code> 来比较)。</p>
<h3 id="52">处理同一个错误两次 (#52)</h3> <h3 id="52">两次处理同一个错误 (#52)</h3>
<p>大多数情况下,错误仅需要处理一次。打印错误日志也是一种错误处理。因此,当函数内发生错误时,应该在打印日志和返回错误中选择其中一种。包装错误也可以提供问题发生的额外上下文信息,也包括了原来的错误(可考虑交给调用方负责打日志)。</p> <p>大多数情况下,错误仅需要处理一次。打印错误日志也是一种错误处理。因此,当函数内发生错误时,应该在打印日志和返回错误中选择其中一种。包装错误也可以提供问题发生的额外上下文信息,也包括了原来的错误(可考虑交给调用方负责打日志)。</p>
<h3 id="53">不处理错误 (#53)</h3> <h3 id="53">不处理错误 (#53)</h3>
<p>不管是在函数调用时,还是在一个 <code>defer</code> 函数执行时,如果想要忽略一个错误,应该显地通过 <code>_</code> 来忽略(可注明忽略的原因)。否则,将来的读者就会感觉到困惑,忽略这个错误是有意为之还是无意中漏掉了。</p> <p>不管是在函数调用时,还是在一个 <code>defer</code> 函数执行时,如果想要忽略一个错误,应该显地通过 <code>_</code> 来忽略(可注明忽略的原因)。否则,将来的读者就会感觉到困惑,忽略这个错误是有意为之还是无意中漏掉了。</p>
<h3 id="defer-54">不处理 <code>defer</code> 中的错误 (#54)</h3> <h3 id="defer-54">不处理 <code>defer</code> 中的错误 (#54)</h3>
<p>大多数情况下,你不应该忽略 <code>defer</code> 函数执行时返回的错误,或者显处理它,或者将它传递给调用方处理,可以根据情景进行选择。如果你确定要忽略这个错误,请显使用 <code>_</code> 来忽略。</p> <p>大多数情况下,你不应该忽略 <code>defer</code> 函数执行时返回的错误,或者显处理它,或者将它传递给调用方处理,可以根据情景进行选择。如果你确定要忽略这个错误,请显使用 <code>_</code> 来忽略。</p>
<h2 id="_7">并发编程: 基础</h2> <h2 id="_7">并发编程: 基础</h2>
<h3 id="55">混淆并发和并行 (#55)</h3> <h3 id="55">混淆并发和并行 (#55)</h3>
<p>理解并发concurrency、并行parallelism之间的本质区别是 Go 开发人员必须要掌握的。并发是关于结构设计上的,并行是关于具体执行上的。</p> <p>理解并发concurrency、并行parallelism之间的本质区别是 Go 开发人员必须要掌握的。并发是关于结构设计上的,并行是关于具体执行上的。</p>
<h3 id="56"><a href="https://100go.co/56-concurrency-faster/">认为并发总是更快</a> (#56)</h3> <h3 id="56"><a href="https://100go.co/56-concurrency-faster/">认为并发总是更快</a> (#56)</h3>
<p>要成为一名熟练的开发人员,您必须意识到并非所有场景下都是并发的方案更快。对于任务中的最小工作负载部分,对它们进行并行化处理并不一定就有明显收益或者比串行化方案更快。对串行化、并发方案进行 benchmark 测试,是验证假设的好办法。</p> <p>要成为一名熟练的开发人员,您必须意识到并非所有场景下都是并发的方案更快。对于任务中的最小工作负载部分,对它们进行并行化处理并不一定就有明显收益或者比串行化方案更快。对串行化、并发方案进行 benchmark 测试,是验证假设的好办法。</p>
<h3 id="channelsmutexes-57">不清楚何时使用channels或mutexes (#57)</h3> <h3 id="channels-mutexes-57">不清楚何时使用 channels 或 mutexes (#57)</h3>
<p>了解 goroutine 之间的交互也可以在选择使用 channels 或 mutexes 时有所帮助。一般来说,并行的 goroutine 需要同步,因此需要使用 mutexes。相反并发的 goroutine 通常需要协调和编排,因此需要使用 channels。</p> <p>了解 goroutine 之间的交互也可以在选择使用 channels 或 mutexes 时有所帮助。一般来说,并行的 goroutine 需要同步,因此需要使用 mutexes。相反并发的 goroutine 通常需要协调和编排,因此需要使用 channels。</p>
<h3 id="vs-go-58">不明白竞态问题 (数据竞态 vs. 竞态条件和 Go 内存模型) (#58)</h3> <h3 id="vs-go-58">不明白竞态问题 (数据竞态 vs. 竞态条件和 Go 内存模型) (#58)</h3>
<p>掌握并发意味着要认识到数据竞争data races和竞态条件race conditions是两个不同的概念。数据竞争指的是有多个goroutines同时访问相同内存区域时没有必要的同步控制且其中至少有一个goroutine是执行的写操作。同时要认识到,没有发生数据竞争不代表程序的执行是确定性的、没问题的。当在某个特定的操作顺序或者特定的事件发生顺序下,如果最终的行为是不可控的,这就是竞态条件。</p> <p>掌握并发意味着要认识到数据竞争data races和竞态条件race conditions是两个不同的概念。数据竞争指的是有多个 goroutines 同时访问相同内存区域时,缺乏必要的同步控制,且其中至少有一个 goroutine 执行的是写操作。同时要认识到,没有发生数据竞争不代表程序的执行是确定性的、没问题的。当在某个特定的操作顺序或者特定的事件发生顺序下,如果最终的行为是不可控的,这就是竞态条件。</p>
<blockquote> <blockquote>
<p>ps数据竞争是竞态条件的子集竞态条件不仅局限于访存未同步它可以发生在更高的层面。<code>go test -race</code> 检测的是数据竞争,需要同步来解决,而开发者还需要关注面更广的竞态条件,它需要对多个 goroutines 的执行进行编排。</p> <p>ps数据竞争是竞态条件的子集竞态条件不仅局限于访存未同步它可以发生在更高的层面。<code>go test -race</code> 检测的是数据竞争,需要同步来解决,而开发者还需要关注面更广的竞态条件,它需要对多个 goroutines 的执行进行编排。</p>
</blockquote> </blockquote>
<p>理解 Go 的内存模型以及有关顺序和同步的底层保证是防止可能的数据竞争和竞态条件的关键。</p> <p>理解 Go 的内存模型以及有关顺序和同步的底层保证是防止可能的数据竞争和竞态条件的关键。</p>
<h3 id="59">不理解不同工作负载类型对并发的影响 (#59)</h3> <h3 id="59">不理解不同工作负载类型对并发的影响 (#59)</h3>
<p>当创建一定数量的goroutines是需要考虑工作负载的类型。如果工作负载是CPU密集型的那么goroutines数量应该接近于 <code>GOMAXPROCS</code> 的值该值取决于主机处理器核心数。如果工作负载是IO密集型的goroutines数量就需要考虑多种因素比如外部系统考虑请求、响应速率</p> <p>当创建一定数量的 goroutines 时,需要考虑工作负载的类型。如果工作负载是 CPU 密集型的,那么 goroutines 数量应该接近于 <code>GOMAXPROCS</code> 的值(该值取决于主机处理器核心数)。如果工作负载是 IO 密集型的goroutines 数量就需要考虑多种因素,比如外部系统(考虑请求、响应速率)。</p>
<h3 id="go-contexts-60">误解了 Go contexts (#60)</h3> <h3 id="go-contexts-60">误解了 Go contexts (#60)</h3>
<p>Go 的上下文context也是 Go 并发编程的基石之一。上下文允许您携带截止时间、取消信号和键值列表。</p> <p>Go 的上下文context也是 Go 并发编程的基石之一。上下文允许您携带截止时间、取消信号和键值列表。</p>
<h2 id="_8">并发编程: 实践</h2> <h2 id="_8">并发编程: 实践</h2>
@ -3334,13 +3334,13 @@
<h3 id="goroutine-62">启动了一个 goroutine 但是不知道它何时会停止 (#62)</h3> <h3 id="goroutine-62">启动了一个 goroutine 但是不知道它何时会停止 (#62)</h3>
<p>避免 goroutine 泄漏,要有这种意识,当创建并启动一个 goroutine 的时候,应该有对应的设计让它能正常退出。</p> <p>避免 goroutine 泄漏,要有这种意识,当创建并启动一个 goroutine 的时候,应该有对应的设计让它能正常退出。</p>
<h3 id="goroutines-63">不注意处理 goroutines 和循环中的迭代变量 (#63)</h3> <h3 id="goroutines-63">不注意处理 goroutines 和循环中的迭代变量 (#63)</h3>
<p>为了避免goroutines和循环中的迭代变量问题可以考虑创建局部变量并将迭代变量赋值给局部变量或者goroutine调用带参数的函数将迭代变量值作为参数值传入来代替goroutine调用闭包。</p> <p>为了避免 goroutines 和循环中的迭代变量问题,可以考虑创建局部变量并将迭代变量赋值给局部变量,或者 goroutines 调用带参数的函数,将迭代变量值作为参数值传入,来代替 goroutines 调用闭包。</p>
<h3 id="select-channels-64">使用 select + channels 时误以为分支选择顺序是确定的 (#64)</h3> <h3 id="select-channels-64">使用 select + channels 时误以为分支选择顺序是确定的 (#64)</h3>
<p>要明白,<code>select</code> 多个 channels 时,如果多个 channels 上的操作都就绪,那么会随机选择一个 <code>case</code> 分支来执行,因此要避免有分支选择顺序是从上到下的这种错误预设,这可能会导致设计上的 bug。</p> <p>要明白,<code>select</code> 多个 channels 时,如果多个 channels 上的操作都就绪,那么会随机选择一个 <code>case</code> 分支来执行,因此要避免有分支选择顺序是从上到下的这种错误预设,这可能会导致设计上的 bug。</p>
<h3 id="channels-65">不正确使用通知 channels (#65)</h3> <h3 id="channels-65">不正确使用通知 channels (#65)</h3>
<p>发送通知时使用 <code>chan struct{}</code> 类型。</p> <p>发送通知时使用 <code>chan struct{}</code> 类型。</p>
<blockquote> <blockquote>
<p>ps: 先明白什么是通知channels一个通知channels指的是只是用来做通知而其中传递的数据没有意义或者理解成不传递数据的channels这种称为通知channels。其中传递的数据的类型struct{}更合适。</p> <p>ps: 先明白什么是通知 channels一个通知 channels 指的是只是用来做通知,而其中传递的数据没有意义,或者理解成不传递数据的 channels这种称为通知 channels。其中传递的数据的类型struct{} 更合适。</p>
</blockquote> </blockquote>
<h3 id="nil-channels-66">不使用 nil channels (#66)</h3> <h3 id="nil-channels-66">不使用 nil channels (#66)</h3>
<p>使用 nil channels 应该是并发处理方式中的一部分,例如,它能够帮助禁用 <code>select</code> 语句中的特定的分支。</p> <p>使用 nil channels 应该是并发处理方式中的一部分,例如,它能够帮助禁用 <code>select</code> 语句中的特定的分支。</p>
@ -3353,14 +3353,14 @@
<h3 id="etcd-68">忘记了字符串格式化可能带来的副作用(例如 etcd 数据竞争和死锁)(#68)</h3> <h3 id="etcd-68">忘记了字符串格式化可能带来的副作用(例如 etcd 数据竞争和死锁)(#68)</h3>
<p>意识到字符串格式化可能会导致调用现有函数,这意味着需要注意可能的死锁和其他数据竞争问题。</p> <p>意识到字符串格式化可能会导致调用现有函数,这意味着需要注意可能的死锁和其他数据竞争问题。</p>
<blockquote> <blockquote>
<p>ps: 核心是要关注 <code>fmt.Sprintf</code> + <code>%v</code> 进行字符串格式化时 <code>%v</code> 具体到不同的类型值时,实际上执行的操作是什么。比如 <code>%v</code> 这个placeholder对应的值时一个 <code>context.Context</code>,那么会就遍历其通过 <code>context.WithValue</code> 附加在其中的 values这个过程可能涉及到数据竞争问题。书中提及的另一个导致死锁的案例本质上也是一样的问题只不过又额外牵扯到了 <code>sync.RWMutex</code> 不可重入的问题。</p> <p>ps: 核心是要关注 <code>fmt.Sprintf</code> + <code>%v</code> 进行字符串格式化时 <code>%v</code> 具体到不同的类型值时,实际上执行的操作是什么。比如 <code>%v</code> 这个 placeholder 对应的值是一个 <code>context.Context</code>,那么会就遍历其通过 <code>context.WithValue</code> 附加在其中的 values这个过程可能涉及到数据竞争问题。书中提及的另一个导致死锁的案例本质上也是一样的问题只不过又额外牵扯到了 <code>sync.RWMutex</code> 不可重入的问题。</p>
</blockquote> </blockquote>
<h3 id="append-69">使用 append 不当导致数据竞争 (#69)</h3> <h3 id="append-69">使用 append 不当导致数据竞争 (#69)</h3>
<p>调用 <code>append</code> 不总是没有数据竞争的,因此不要在一个共享的 <code>slice</code> 上并发地执行 <code>append</code></p> <p>调用 <code>append</code> 不总是没有数据竞争的,因此不要在一个共享的 <code>slice</code> 上并发地执行 <code>append</code></p>
<h3 id="mutexes-slicesmaps-70">误用 mutexes 和 slices、maps (#70)</h3> <h3 id="mutexes-slicesmaps-70">误用 mutexes 和 slices、maps (#70)</h3>
<p>请记住 slices 和 maps 引用类型,有助于避免常见的数据竞争问题。</p> <p>请记住 slices 和 maps 引用类型,有助于避免常见的数据竞争问题。</p>
<blockquote> <blockquote>
<p>ps: 这里实际是因为错误理解了 slices 和 maps导致写出了错误拷贝 slices 和 maps 的代码,进而导致锁保护无效、出现数据竞争问题。</p> <p>ps: 这里实际是因为错误理解了 slices 和 maps导致写出了错误拷贝 slices 和 maps 的代码,进而导致锁保护无效、出现数据竞争问题。</p>
</blockquote> </blockquote>
<h3 id="syncwaitgroup-71">误用 <code>sync.WaitGroup</code> (#71)</h3> <h3 id="syncwaitgroup-71">误用 <code>sync.WaitGroup</code> (#71)</h3>
<p>正确地使用 <code>sync.WaitGroup</code> 需要在启动 goroutines 之前先调用 <code>Add</code> 方法。</p> <p>正确地使用 <code>sync.WaitGroup</code> 需要在启动 goroutines 之前先调用 <code>Add</code> 方法。</p>
@ -3372,7 +3372,7 @@
<p><code>sync</code> 包下的类型不应该被拷贝。</p> <p><code>sync</code> 包下的类型不应该被拷贝。</p>
<h2 id="_9">标准库</h2> <h2 id="_9">标准库</h2>
<h3 id="timeduration-75">使用了错误的 time.Duration (#75)</h3> <h3 id="timeduration-75">使用了错误的 time.Duration (#75)</h3>
<p>注意有些函数接收一个 <code>time.Duration</code> 类型的参数时,尽管直接传递一个整数是可以的,但最好还是使用 time API 中的方法来传递 duration以避免可能造成的困惑bug。</p> <p>注意有些函数接收一个 <code>time.Duration</code> 类型的参数时,尽管直接传递一个整数是可以的,但最好还是使用 time API 中的方法来传递 duration以避免可能造成的困惑bug。</p>
<blockquote> <blockquote>
<p>ps: 重点是注意 time.Duration 定义的是 nanoseconds 数。</p> <p>ps: 重点是注意 time.Duration 定义的是 nanoseconds 数。</p>
</blockquote> </blockquote>
@ -3382,7 +3382,7 @@
<ul> <ul>
<li>类型嵌套导致的预料外的行为</li> <li>类型嵌套导致的预料外的行为</li>
</ul> </ul>
<p>要当心在Go结构体中嵌入字段这样做可能会导致诸如嵌入的 <code>time.Time</code> 字段实现 <code>json.Marshaler</code> 接口,从而覆盖默认的json序列。</p> <p>要当心在 Go 结构体中嵌入字段,这样做可能会导致诸如嵌入的 <code>time.Time</code> 字段实现 <code>json.Marshaler</code> 接口,从而覆盖默认的 JSON 序列。</p>
<ul> <ul>
<li>JSON 和单调时钟</li> <li>JSON 和单调时钟</li>
</ul> </ul>
@ -3412,11 +3412,11 @@
<li>不处理行迭代时的错误</li> <li>不处理行迭代时的错误</li>
</ul> </ul>
<p>调用 <code>sql.Rows</code><code>Err</code> 方法来确保在准备下一个行时没有遗漏错误。</p> <p>调用 <code>sql.Rows</code><code>Err</code> 方法来确保在准备下一个行时没有遗漏错误。</p>
<h3 id="http-sqlrows-osfile-79">不关闭临时资源HTTP 请求体、<code>sql.Rows</code><code>os.File</code>) (#79)</h3> <h3 id="http-sqlrows-osfile-79">不关闭临时资源HTTP 请求体、<code>sql.Rows</code><code>os.File</code> (#79)</h3>
<p>最终要注意关闭所有实现 <code>io.Closer</code> 接口的结构体,以避免可能的泄漏。</p> <p>最终要注意关闭所有实现 <code>io.Closer</code> 接口的结构体以避免可能的泄漏。</p>
<h3 id="http-80">响应 HTTP 请求后没有返回语句 (#80)</h3> <h3 id="http-80">响应 HTTP 请求后没有返回语句 (#80)</h3>
<p>为了避免在 HTTP 处理函数中出现某些意外的问题,如果想在发生 <code>http.Error</code> 后让 HTTP 处理函数停止,那么就不要忘记使用 <code>return</code> 语句来阻止后续代码的执行。</p> <p>为了避免在 HTTP 处理函数中出现某些意外的问题,如果想在发生 <code>http.Error</code> 后让 HTTP 处理函数停止,那么就不要忘记使用 <code>return</code> 语句来阻止后续代码的执行。</p>
<h3 id="http-clientserver-81">直接使用默认的HTTP client和server (#81)</h3> <h3 id="http-client-server-81">直接使用默认的 HTTP client 和 server (#81)</h3>
<p>对于生产级别的应用,不要使用默认的 HTTP client 和 server 实现。这些实现缺少超时和生产环境中应该强制使用的行为。</p> <p>对于生产级别的应用,不要使用默认的 HTTP client 和 server 实现。这些实现缺少超时和生产环境中应该强制使用的行为。</p>
<h2 id="_10">测试</h2> <h2 id="_10">测试</h2>
<h3 id="build-tags-82">不对测试进行分类 build tags, 环境变量,短模式)(#82)</h3> <h3 id="build-tags-82">不对测试进行分类 build tags, 环境变量,短模式)(#82)</h3>
@ -3425,12 +3425,12 @@
<p>ps: 了解下 go build tags以及 <code>go test -short</code></p> <p>ps: 了解下 go build tags以及 <code>go test -short</code></p>
</blockquote> </blockquote>
<h3 id="race-83">不打开 race 开关 (#83)</h3> <h3 id="race-83">不打开 race 开关 (#83)</h3>
<p>打开 <code>-race</code> 开关在编写并发应用时非常重要。这能帮助你捕获可能的数据竞争,从而避免软件bug。</p> <p>打开 <code>-race</code> 开关在编写并发应用时非常重要。这能帮助你捕获可能的数据竞争,从而避免软件 bug。</p>
<h3 id="parallel-shuffle-84">不打开测试的执行模式开关 (parallel 和 shuffle) (#84)</h3> <h3 id="parallel-shuffle-84">不打开测试的执行模式开关 (parallel 和 shuffle) (#84)</h3>
<p>打开开关 <code>-parallel</code> 有助于加速测试的执行,特别是测试中包含一些需要长期运行的用例的时候。</p> <p>打开开关 <code>-parallel</code> 有助于加速测试的执行,特别是测试中包含一些需要长期运行的用例的时候。</p>
<p>打开开关 <code>-shuffle</code> 能够打乱测试用例执行的顺序,避免一个测试依赖于某些不符合真实情况的预设,有助于及早暴bug。</p> <p>打开开关 <code>-shuffle</code> 能够打乱测试用例执行的顺序,避免一个测试依赖于某些不符合真实情况的预设,有助于及早暴bug。</p>
<h3 id="85">不使用表驱动的测试 (#85)</h3> <h3 id="85">不使用表驱动的测试 (#85)</h3>
<p>表驱动的测试是一种有效的方式,可以将一组相似的测试分组在一起,以避免代码重复和使未来的更新更容易处理。</p> <p>表驱动的测试是一种有效的方式,可以将一组相似的测试分组在一起,以避免代码重复和使未来的更新更容易处理。</p>
<h3 id="sleep-86">在单元测试中执行 sleep 操作 (#86)</h3> <h3 id="sleep-86">在单元测试中执行 sleep 操作 (#86)</h3>
<p>使用同步的方式、避免 sleep来尽量减少测试的不稳定性和提高鲁棒性。如果无法使用同步手段,可以考虑重试的方式。</p> <p>使用同步的方式、避免 sleep来尽量减少测试的不稳定性和提高鲁棒性。如果无法使用同步手段,可以考虑重试的方式。</p>
<h3 id="time-api-87">没有高效地处理 time API (#87)</h3> <h3 id="time-api-87">没有高效地处理 time API (#87)</h3>
@ -3447,7 +3447,7 @@
<li>做出错误的微基准测试假设</li> <li>做出错误的微基准测试假设</li>
</ul> </ul>
<p>增加 <code>benchtime</code> 或者使用 <code>benchstat</code> 等工具可以有助于微基准测试。</p> <p>增加 <code>benchtime</code> 或者使用 <code>benchstat</code> 等工具可以有助于微基准测试。</p>
<p>小心微基准测试的结果,如果最终运行应用程序的系统与运行微基准测试的系统不同。</p> <p>小心微基准测试的结果如果最终运行应用程序的系统与运行微基准测试的系统不同。</p>
<ul> <ul>
<li>对编译期优化要足够小心</li> <li>对编译期优化要足够小心</li>
</ul> </ul>
@ -3455,20 +3455,20 @@
<ul> <ul>
<li>被观察者效应所欺骗</li> <li>被观察者效应所欺骗</li>
</ul> </ul>
<p>为了避免被观察者效应欺骗,强制重新创建CPU密集型函数使用的数据。</p> <p>为了避免被观察者效应欺骗强制重新创建CPU密集型函数使用的数据。</p>
<h3 id="go-test-90">没有去探索 go test 所有的特性 (#90)</h3> <h3 id="go-test-90">没有去探索 go test 所有的特性 (#90)</h3>
<ul> <ul>
<li>代码覆盖率</li> <li>代码覆盖率</li>
</ul> </ul>
<p>使用 <code>-coverprofile</code> 参数可以快速查看代码的测试覆盖情况,方便快速查看哪个部分需要更多的关注。</p> <p>使用 <code>-coverprofile</code> 参数可以快速查看代码的测试覆盖情况,方便快速查看哪个部分需要更多的关注。</p>
<ul> <ul>
<li>一个不同包中执行测试</li> <li>在不同包中执行测试</li>
</ul> </ul>
<p>单元测试组织到一个独立的包中,对于对外层暴的接口,需要写一些测试用例。测试应该关注公开的行为,而非内部实现细节。</p> <p>单元测试组织到一个独立的包中,对于对外层暴的接口,需要写一些测试用例。测试应该关注公开的行为,而非内部实现细节。</p>
<ul> <ul>
<li>Utility 函数</li> <li>Utility 函数</li>
</ul> </ul>
<p>处理错误时,使用 <code>*testing.T</code> 变量而不是经典的 <code>if err != nil</code> 可以让代码更加简洁易读。</p> <p>处理错误时使用 <code>*testing.T</code> 变量而不是经典的 <code>if err != nil</code> 可以让代码更加简洁易读。</p>
<ul> <ul>
<li>设置和销毁</li> <li>设置和销毁</li>
</ul> </ul>
@ -3507,19 +3507,19 @@
<h3 id="93">没有考虑指令级的并行 (#93)</h3> <h3 id="93">没有考虑指令级的并行 (#93)</h3>
<p>使用指令级并行(<abbr title="Instruction-Level Parallelism">ILP</abbr>)优化代码的特定部分,以允许 CPU 尽可能执行更多可以并行执行的指令。识别指令的数据依赖问题data hazards是主要步骤之一。</p> <p>使用指令级并行(<abbr title="Instruction-Level Parallelism">ILP</abbr>)优化代码的特定部分,以允许 CPU 尽可能执行更多可以并行执行的指令。识别指令的数据依赖问题data hazards是主要步骤之一。</p>
<h3 id="94">不了解数据对齐 (#94)</h3> <h3 id="94">不了解数据对齐 (#94)</h3>
<p>记住Go中基本类型与其自身大小对齐例如按降序从大到小重新组织结构体的字段可以形成更紧凑的结构体(减少内存分配,更好的空间局部性),这有助于避免一些常见的错误。</p> <p>记住 Go 中基本类型与其自身大小对齐,例如,按大小降序重新组织结构体的字段可以形成更紧凑的结构体(减少内存分配,更好的空间局部性),这有助于避免一些常见的错误。</p>
<h3 id="stack-vs-heap-95">不了解 stack vs. heap (#95)</h3> <h3 id="stack-vs-heap-95">不了解 stack vs. heap (#95)</h3>
<p>了解堆和栈之间的区别是开发人员的核心知识点特别是要去优化一个Go程序时。栈分配的开销几乎为零而堆分配则较慢并且依赖GC来清理内存。</p> <p>了解堆和栈之间的区别是开发人员的核心知识点,特别是要去优化一个 Go 程序时。栈分配的开销几乎为零,而堆分配则较慢,并且依赖 <abbr title="Garbage Collector">GC</abbr> 来清理内存。</p>
<h3 id="api-compiler-optimizations-and-syncpool-96">不知道如何减少内存分配次数 (API调整, compiler optimizations, and <code>sync.Pool</code>) (#96)</h3> <h3 id="api-syncpool-96">不知道如何减少内存分配次数API 调整,编译器优化和 <code>sync.Pool</code> (#96)</h3>
<p>减少内存分配次数也是优化Go应用的一个重要方面。这可以通过不同的方式来实现,比如仔细设计API来避免不必要的拷贝,以及使用 <code>sync.Pool</code> 来对分配对象进行池化。</p> <p>减少内存分配次数也是优化 Go 应用的一个重要方面。这可以通过不同的方式来实现,比如仔细设计 API 来避免不必要的拷贝,以及使用 <code>sync.Pool</code> 来对分配对象进行池化。</p>
<h3 id="97">不注意使用内联 (#97)</h3> <h3 id="97">不注意使用内联 (#97)</h3>
<p>使用快速路径的内联技术来更加有效地减少调用函数的摊销时间。</p> <p>使用快速路径的内联技术来更加有效地减少调用函数的摊销时间。</p>
<h3 id="go-98"><a href="https://100go.co/98-profiling-execution-tracing/">不使用 Go 问题诊断工具</a> (#98)</h3> <h3 id="go-98"><a href="https://100go.co/98-profiling-execution-tracing/">不使用 Go 问题诊断工具</a> (#98)</h3>
<p>了解Go profilng工具、执行时tracer来辅助判断一个应用程序是否正常以及列出需要优化的部分。</p> <p>了解 Go profiling 工具、执行时 tracer 来辅助判断一个应用程序是否正常,以及列出需要优化的部分。</p>
<h3 id="gc-99">不理解GC是如何工作的 (#99)</h3> <h3 id="gc-99">不理解 <abbr title="Garbage Collector">GC</abbr> 是如何工作的 (#99)</h3>
<p>理解如何调优GC能够带来很多收益,例如有助于更高效地处理突增的负载。</p> <p>理解如何调优 <abbr title="Garbage Collector">GC</abbr> 能够带来很多收益,例如有助于更高效地处理突增的负载。</p>
<h3 id="dockerk8sgo-100">不了解Docker或者K8S对运行的Go应用的性能影响 (#100)</h3> <h3 id="docker-k8s-go-100">不了解 Docker 或者 K8S 对运行的 Go 应用的性能影响 (#100)</h3>
<p>为了避免CPU throttlingCPU限频问题当我们在Docker和Kubernetes部署应用时要知道Go语言对CFS(完全公平调度器)无感知。</p> <p>为了避免 CPU throttlingCPU 限频)问题,当我们在 Docker 和 Kubernetes 部署应用时,要知道 Go 语言对 <abbr title="Completely Fair Scheduler">CFS</abbr>(完全公平调度器)无感知。</p>