Go言語は正確には何に適していますか?

Go言語開発チームは、今日のソフトウェア開発者が直面している問題を解決するのに長い時間を費やしました。プロジェクトの言語を選択するとき、開発者は迅速な開発とパフォーマンスのどちらかを選択する必要があります。CやC ++などの言語は実行速度が速く、RubyやPythonなどの言語は迅速な開発が得意です。Go言語は2つの間に橋渡しをし、高性能言語を提供するだけでなく、開発を高速化します。

Go言語を探索する過程で、読者は適切に設計された機能と簡潔な構文を見ることができます。言語として、Goは実行できることだけでなく、実行できないことも定義します。Go言語の構文は、いくつかのキーワードのみに簡潔であり、覚えやすいです。Go言語のコンパイラーは非常に高速であり、コンパイルする気にならない人もいます。したがって、Go開発者はプロジェクトのビルドを待機する時間を大幅に削減できます。Go言語には組み込みの同時実行メカニズムがあるため、特定のスレッドライブラリを使用せずにソフトウェアを拡張して、より多くのリソースを使用できます。Go言語の型システムはシンプルで効率的であり、オブジェクト指向開発に追加の考え方を必要としないため、開発者はコードの再利用に集中できます。Go言語にはガベージコレクターも付属しており、ユーザーが自分でメモリを管理する必要はありません。これらの主要な機能を簡単に見てみましょう。

1.1.1開発速度

大規模なCまたはC ++プロジェクトのコンパイルにかかる時間は、コーヒーを飲む時間よりもさらに長くなります。図1-1は、オフィスで脱出するための古典的な言い訳を説明するXKCDの漫画です。

 

図1-1よく働きますか?(XKCDから)

Go言語はよりスマートなコンパイラを使用し、依存関係を解決するためのアルゴリズムを簡素化し、最終的にコンパイル速度を向上させます。Goプログラムをコンパイルする場合、コンパイラーは、Java、C、C ++などの依存関係チェーン内のすべての依存ライブラリーを走査するのではなく、直接参照されるライブラリーにのみ注意を払います。したがって、多くのGoプログラムは1秒でコンパイルできます。最新のハードウェアでは、Go言語のソースツリー全体をコンパイルするのに20秒しかかかりません。

コードのコンパイルからコードの実行までの中間プロセスがないため、動的言語でアプリケーションを作成すると、出力をすばやく確認できます。代償として、動的言語は静的言語によって提供されるタイプセーフ機能を提供せず、実行時のタイプエラーなどのバグを回避するために多数のテストスイートを使用する必要があります。

JavaScriptのような動的言語で大きなアプリケーションを開発していて、関数がIDと呼ばれるフィールドを受け取ることを想定していると想像してください。このパラメーターは、整数、文字列、またはUUIDのどれにする必要がありますか?答えを知るには、ソースコードしか見ることができません。この関数を数値または文字列で実行して、何が起こるかを確認できます。Go言語では、コンパイラーがこのタイプのエラーをキャッチするのを助けることができるので、これを心配する必要はありません。

1.1.2並行性

プログラマーとして、ハードウェアリソースを最大限に活用できるアプリケーションを開発することは困難です。最近のコンピューターには複数のコアがありますが、ほとんどのプログラミング言語には、プログラムがこれらのリソースを簡単に使用できるようにする効果的なツールがありません。これらの言語は、複数のコアを利用するために多くのスレッド同期コードを記述する必要があり、簡単にエラーが発生する可能性があります。

Go言語の同時実行性のサポートは、この言語の最も重要な機能の1つです。ゴルーチンはスレッドに非常に似ていますが、スレッドよりメモリの消費量がはるかに少なく、使用するコードも少なくて済みます。チャネルは、ユーザーが異なるゴルーチン間でタイプのメッセージを同期的に送信できるようにする組み込みのデータ構造です。これにより、プログラミングモデルは、複数のゴルーチンが同じデータを使用する権利をめぐって競合するのではなく、ゴルーチン間でメッセージを送信する傾向があります。これらの機能の詳細を見てみましょう。

ゴルーチン

ゴルーチンは、他のゴルーチンと並行して、またメインプログラム(プログラムエントリ)と並行して実行できる関数です。他のプログラミング言語では、スレッドを使用して同じことを行う必要がありますが、Goでは、同じスレッドを使用して複数のゴルーチンを実行します。たとえば、ユーザーがWebサーバーを作成していて、同時に異なるWebリクエストを処理したい場合、CまたはJavaを使用する場合、スレッドを使用するには多くの追加コードを作成する必要があります。Go言語では、net / httpライブラリは組み込みのgoroutineを直接使用します。受信した各リクエストは、独自のgoroutineで自動的に処理されます。goroutineはスレッドよりも少ないメモリを使用し、Go言語は実行時に構成済みの論理プロセッサのセットでgoroutineの実行を自動的にスケジュールします。各論理プロセッサは、オペレーティングシステムスレッドにバインドされています(図1-2を参照)。これにより、ユーザーのアプリケーション実行がより効率的になり、開発ワークロードが大幅に削減されます。

 

図1-2単一のシステムスレッドで複数のゴルーチンを実行する

他のことを並行して実行しながらコードを実行したい場合は、goroutineが適しています。以下に簡単な例を示します。

func log(msg string) {
    ...这里是一些记录日志的代码
}

// 代码里有些地方检测到了错误
go log("发生了可怕的事情")

キーワードgoは、記述する必要がある唯一のコードであり、ログ関数は、他のゴルーチンと並行して実行する独立したgoroutineとして実行するようにスケジュールされています。これは、アプリケーションの残りの部分がロギングと並行して実行されることを意味します。これにより、通常、エンドユーザーの気分が良くなります。前述のように、goroutineはより少ないリソースを使用するため、何千ものgoroutineを開始できることがよくあります。第6章では、ゴルーチンと並行性について詳しく説明します。

2。通路

チャネルは、ゴルーチン間の安全なデータ通信を可能にするデータ構造です。チャネルは、他の言語における一般的な共有メモリアクセスの問題を回避するのに役立ちます。

並行性の最も難しい部分は、同時に実行されている他のプロセス、スレッド、またはゴルーチンがユーザーデータを誤って変更しないようにすることです。異なるスレッドが同期保護なしで同じデータを変更すると、災害が常に発生します。他の言語では、グローバル変数または共有メモリを使用する場合、複雑なロックルールを使用して、同じ変数への非同期の変更を防ぐ必要があります。

この問題を解決するために、チャネルは、同時変更中にデータのセキュリティを確保するための新しいモードを提供します。チャンネルA

 

図1-3チャネルを使用してゴルーチン間でデータを安全に送信する

図1-3には、キャッシュのない3つのゴルーチンと2つのチャネルがあります。最初のゴルーチンは、チャネルを介して、すでに待機している2番目のゴルーチンにデータを送信します。2つのゴルーチン間のデータの送信は同期しており、送信が完了すると、両方のゴルーチンはデータが送信されたことを認識します。2番目のgoroutineがこのデータを使用してタスクを完了すると、待機中の3番目のgoroutineにこのデータを渡します。今回は送信がまだ同期されており、両方のゴルーチンがデータ送信が完了したことを確認します。goroutine間でデータを安全に転送するこの方法では、ロックや同期メカニズムは必要ありません。

チャネルは、goroutineを超えたデータアクセス保護メカニズムを提供しないことを強調しておく必要があります。データのコピーがチャネルを介して送信される場合、各goroutineはコピーを保持しており、各goroutineが自分のコピーを変更しても安全です。データへのポインタを送信するときに、読み取りと書き込みが異なるゴルーチンによって行われる場合でも、各ゴルーチンには追加の同期アクションが必要です。

1.1.3 Go言語型システム

Go言語は、動作パフォーマンスを低下させることなくコードを最大限に再利用できる、柔軟性のある非継承型システムを提供します。この型システムは依然としてオブジェクト指向開発をサポートしていますが、従来のオブジェクト指向の問題を回避しています。複雑なJavaおよびC ++プログラムのクラスとインターフェースを抽象化する方法について何週間も費やしたことがあるなら、Go言語の型システムがいかに単純であるかがわかるでしょう。Go開発者はコンポジションデザインパターンを使用し、あるタイプを別のタイプに埋め込むだけで、すべての機能を再利用できます。合成は他の言語でも使用できますが、継承と組み合わせる必要があるため、全体の使用法は非常に複雑で使いにくいものです。Go言語では、継承に基づく従来のモデルを回避するために、他のよりマイナーなタイプの組み合わせによるタイプが作成されます。

さらに、Go言語には、ユーザーがモデリングではなく動作をモデル化できるようにする独自のインターフェース実装メカニズムもあります。Go言語では、特定の型がインターフェースを実装することを宣言する必要はなく、コンパイラーは、型のインスタンスが使用されているインターフェースに準拠しているかどうかを判別します。Go標準ライブラリの多くのインターフェースは非常にシンプルで、いくつかの関数しか開きません。実際には、特にJavaのようなオブジェクト指向言語を使用する人にとって、この機能に慣れるには少し時間がかかります。

1。シンプルタイプ

Go言語には、intやstringなどの組み込み型があるだけでなく、ユーザー定義型もサポートされています。Go言語では、ユーザー定義型には通常、データを格納するための型付きフィールドのセットが含まれています。Go言語のユーザー定義タイプは、C言語の構造と非常によく似ており、使用方法も非常に似ています。ただし、Go言語型では、その型のデータを操作するメソッドを宣言できます。従来の言語では、継承を使用して構造を拡張します。クライアントはユーザーから継承し、ユーザーはエンティティから継承します。Go言語とは異なり、Go開発者は小さなタイプ(顧客と管理者)を作成し、これらの小さなタイプを大きなタイプに結合します。図1-4は、継承と構成の違いを示しています。

 

図1-4継承と構成の比較

2。Goインターフェイスは一連の動作をモデル化します

インターフェイスは、型の動作を説明するために使用されます。タイプのインスタンスがインターフェースを実装する場合、それはインスタンスが特定の動作セットを実行できることを意味します。このインスタンスがインターフェースを実装することを宣言する必要すらなく、この一連の動作を実装するだけで済みます。他の言語では、この機能をアヒルの種類と呼びます。アヒルのように聞こえる場合は、アヒルの可能性があります。Go言語インターフェースも同様です。Go言語では、型がインターフェイスのすべてのメソッドを実装している場合、この型のインスタンスは、追加の宣言なしでインターフェイス型のインスタンスに格納できます。

Javaのような厳密なオブジェクト指向言語では、すべての設計はインターフェースを中心に展開します。コーディングの前に、ユーザーはしばしば巨大な継承チェーンについて考えなければなりません。以下は、Javaインターフェースの例です。

interface User {
    public void login();
    public void logout();
}

このインターフェースをJavaで実装するには、ユーザーのクラスがユーザーインターフェースのすべての制約を満たす必要があり、クラスを明示的に宣言してこのインターフェースを実装する必要があります。Go言語のインターフェースは通常、単一のアクションのみを記述します。Go言語で最も一般的に使用されるインターフェースの1つはio.Readerです。このインターフェイスは、型に読み取るデータがあることを宣言する簡単なメソッドを提供します。標準ライブラリの他の関数は、このインターフェースを理解できます。このインターフェースの定義は次のとおりです。

type Reader interface {
    Read(p []byte) (n int, err error)
}

io.Readerインターフェイスを実装するには、バイトスライスを受け入れて整数とエラーの可能性を返すReadメソッドを実装するだけで済みます。

これは、従来のオブジェクト指向プログラミング言語のインターフェースシステムとは本質的に異なります。Go言語のインターフェースは小さく、単一のアクションのみを定義する傾向があります。実際の使用では、これは組み合わせを使用してコードを再利用するのに役立ちます。ユーザーは、データを含むほとんどすべての型にio.Readerインターフェイスを実装し、この型のインスタンスを、io.Readerの読み取り方法を知っているGo関数に渡すことができます。

Go言語のネットワークライブラリ全体はio.Readerインターフェイスを使用します。これにより、プログラムの機能をさまざまなネットワークの実装から分離できます。このようなインターフェースは楽しく、エレガントで、無料で使用できます。ファイル、バッファ、ソケット、その他のデータソースはすべてio.Readerインターフェイスを実装しています。同じインターフェイスを使用すると、データの出所に関係なく、データを効率的に操作できます。

1.1.4メモリ管理

不適切なメモリ管理は、プログラムのクラッシュやメモリリークを引き起こしたり、オペレーティングシステム全体をクラッシュさせたりする可能性があります。Go言語には、この問題の解決に役立つ最新のガベージコレクションメカニズムがあります。他のシステム言語(CやC ++など)では、このメモリを使用する前に割り当て、使用後に解放する必要があります。間違っていることが1つしかない場合でも、プログラムがクラッシュしたり、メモリがリークしたりすることがあります。残念ながら、メモリがまだ使用されているかどうかを追跡すること自体は非常に難しく、マルチスレッドと高い同時実行性をサポートすることはさらに困難です。Go言語のガベージコレクションには追加のオーバーヘッドがいくつかありますが、プログラミング時の開発の難しさを大幅に減らすことができます。Go言語のデリゲートは、メモリ管理をプロのコンパイラに退屈にして、プログラマがより興味深いことに集中できるようにします。

この記事は「Go Language Practice」からの抜粋です

 

Go言語は、基本的なシステム言語の機能と最新の言語の高度な機能を組み合わせて、シンプルで信頼性が高く効率的なソフトウェアを構築するための障壁を低くすることを目的としています。この本は、読者に焦点を絞った、包括的かつ言語的な見方を提供します。この本は、言語の仕様と実装にも焦点を当てており、関連するコンテンツには、文法、型システム、並行性、パイプライン、テスト、およびその他のトピックが含まれています。

この本は、他のプログラミング言語の基礎と、Goを学びたい開発経験のある中級開発者向けに書かれています。この本は、Go言語の学習を始めたばかりで、Go言語の内部実装について詳しく知りたい人に最適です。

おすすめ

転載: blog.csdn.net/epubit17/article/details/108233560