golang 匿名函數 Anonymous Function

這個例子中,我們定義了三個匿名函式。第一個是最基礎的形式,沒有參數和返回值。第二個帶有參數 x 和 y,並在呼叫時提供了這些參數的值。第三個例子展示了閉包的概念,其中匿名函式可以訪問其外部作用域中的變數 z


package main

import "fmt"

func main() {
// 這是一個簡單的匿名函式
func() {
fmt.Println("這是一個匿名函式!")
}() // 注意最後的 (),這是用來呼叫這個匿名函式的

// 匿名函式也可以接受參數
func(x int, y int) {
fmt.Println(x + y)
}(3, 4) // 輸出 7

// 閉包:匿名函式可以存取外部的變量
z := 10
f := func() {
fmt.Println(z)
}
f() // 輸出 10
}

Concurrency in go 讀書心得 2.Concurrency ? Parallelism?

 

Concurrency 與 Parallelism 的深入探討:從差異到應用

我們常常聽到兩個詞:Concurrency(併發)與 Parallelism(平行)。儘管這兩個詞語經常被交替使用,但它們代表的概念是有所不同的。理解這兩者的差異不僅有助於技術深入,還有助於我們在設計和開發時做出明智的選擇。本文將試圖剖析這兩者的核心差異,並提供一些應用的例子。

1. Concurrency(併發)

Concurrency主要關心的是多個任務可以在同一時間段內被啟動或完成,但並不意味著這些任務是同時執行的。它是一種如何結構化程序、算法,以便有效地處理多件事情,這些事情可能(但不必)在同一時間發生。

舉個例子,一位雜技演員正在同時擲多個球,儘管每次只接觸一個球,但對觀眾來說,似乎球都在「同時」被擲。這就是concurrency的概念:雖然一次只處理一件事,但在整個過程中處理了多件事情。

2. Parallelism(平行)

平行則是指多個任務或計算過程在同一時刻真正同時進行。這通常需要多個處理器或核心,或者是分佈式的系統。

再次以例子來說明:想象一個跑道,有四名運動員同時起跑,他們同時向終點前進。這是真正的「同時」,每個運動員的跑動都是同時發生的,而不是輪流。

併發與平行的關聯

這兩者之間的關係是密不可分的。併發可以在單核系統中實現,切換上下文以模擬多個任務「似乎」同時運行。然而,當有多核或多處理器可用時,併發任務也可能平行運行。

舉例說明,當我們開啟多個網頁標籤時,這些標籤可能是併發加載的,但若我們的系統有多個核心,則可能會有多個標籤同時加載,達到平行。

結論

Concurrency 和 Parallelism 雖然有所區別,但它們共同的目的是提高效率和性能。理解它們的差異以及如何適當地應用它們,可以幫助我們設計出更加高效和響應迅速的系統和應用程序。當我們說某系統支援併發時,它可能不真正做到「同時」的執行;但當說到平行時,它真的做到了多個操作的同時進行。

Concurrency in go 讀書心得 1.前言

 

動機:希望加深對於golang concurrency的理解

去年,我也報名了ithome鐵人賽。當初帶著滿腔熱誠,想要透過連續的寫作分享知識。然而,生活總是充滿了變數。原以為能夠穩定發文的計畫,因為突然需要跟主管一起出差一週而被打破。但,過去就是過去,我認為最重要的是如何從過去的經驗中學習,並將之轉化為未來更強烈的動力。

因此,今年我決定再度參賽,但這次的目標更加明確:對Go語言的併發(Concurrency)進行深入探討。選擇這個主題,背後有兩大原因。首先,併發是Go語言中非常強大,但也相對複雜的特性。我深知為了真正理解一個技術,最好的方法就是教學以致用,透過撰寫系列文章的方式,透過將知識傳達給他人,同時鞏固自己的認知。

這次的寫作,我主要參考了一本名為《Concurrency in Go》的書籍。這本書深入淺出地探討了Go語言中併發的相關知識。我會將這些知識與心得整理成文章分享給大家,我認為這種從理論到實踐,再從實踐回到理論的過程,是學習的最佳循環。

另外,我也認為這次的挑戰不僅僅是技術層面的。時間管理、保持寫作的習慣、應對突如其來的變故......這都是參與鐵人賽所必須面對的挑戰。去年的經驗讓我明白,任何事情都有可能發生。所以,這次我已做足準備,不論是事先撰寫一些草稿,還是找尋靈感的方法,都已經備妥。

總結來說,參與鐵人賽不僅僅是技術的分享,更是一個挑戰自己的機會。我期待透過這30天的旅程,不只能與大家分享Go語言併發的魅力,更希望能夠完成我自己的小目標,彌補去年的遺憾。期待在這次的賽程中,能夠與各位一同學習、進步,共同邁向更高的技術境界。


Websocket連線

WebSocket 是一個先進的技術,允許在用戶端和伺服器之間建立互動式通訊會話。以下是 WebSocket 在實際使用上的優點:


1. **雙向通信**: WebSocket 提供了全雙工的通信管道。這意味著伺服器和客戶端都可以同時發送和接收資料,而不需要等待對方完成。

   

2. **即時性**: 因為它是一個持久的連接,所以資料可以立即無延遲地傳輸。

   

3. **減少網絡流量**: 與傳統的 HTTP 請求相比,WebSocket 不需要每次交換數據都發送完整的 HTTP 頭部資料,這可以減少不必要的帶寬使用和延遲。

   

4. **低延遲**: WebSocket 適合於需要低延遲的應用,例如遊戲、財經交易平台或實時訊息系統。

   

5. **持久的連接**: 一旦 WebSocket 連接建立,它將保持打開狀態,直到客戶端或伺服器決定關閉它。這與傳統的 HTTP 連接不同,後者會在每次請求後被關閉。

   

6. **適應性**: WebSocket 可以通過已存在的 HTTP 或 HTTPS 端口運行,這使得它可以輕鬆地整合到現有的 IT 基礎設施中。


當 WebSocket 首次建立連接時,它會開始一個稱為 "握手" 的過程。這基本上是一個標準的 HTTP 請求,但帶有一些特殊的頭部資訊,表明客戶端希望建立 WebSocket 連接而不是普通的 HTTP 連接。


1. **客戶端請求**:客戶端發送一個 HTTP 請求到伺服器,這個請求看起來大致像這樣:


   ```

   GET /mychat HTTP/1.1

   Host: server.example.com

   Upgrade: websocket

   Connection: Upgrade

   Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==

   Sec-WebSocket-Protocol: chat, superchat

   Sec-WebSocket-Version: 13

   Origin: http://example.com

   ```


   這裡,"Upgrade: websocket" 和 "Connection: Upgrade" 告訴伺服器,客戶端希望升級協議從 HTTP 到 WebSocket。


2. **伺服器回應**:如果伺服器接受此 WebSocket 請求,它會返回以下答覆:


   ```

   HTTP/1.1 101 Switching Protocols

   Upgrade: websocket

   Connection: Upgrade

   Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

   ```


   這裡的 "101 Switching Protocols" 表示伺服器同意切換到 WebSocket 協議。


完成這個交握過程後,連接就會從 HTTP 協議升級到 WebSocket 協議,之後兩者可以開始通過這個通道交換資料。

Byte Code in Java

 

位元碼(Byte Code)

位元碼可以定義為編譯器在編譯源代碼(Java 程式)後生成的中間碼。


位元碼是如何生成的?

Compiler(編譯器)將源代碼或 Java 程式轉換為位元碼(或機器碼),然後解譯器在系統上執行位元碼。解譯器也可以稱為 Java 虛擬機(JVM)。位元碼是編譯器(生成位元碼)和解譯器(執行位元碼)之間的共同部分。


例如

假設您正在撰寫您的第一個 Java 程式。


/*package whatever */

import java.io.*;

  

class GFG {

    public static void main (String[] args) {

        System.out.println("GFG!");

    }

}

輸出

GFG!

上述的程式碼被稱為 Java source code (源代碼)。

編譯器編譯源代碼。

最後,解譯器執行已編譯的源代碼。

無論我們寫任何程式,它都不是以機器碼編寫的。我們使用高階語言如 Java、C++、Python 等來撰寫程式。但是,電腦只能理解機器碼。因此,當我們執行程式時,它首先由編譯器轉換為機器碼或位元碼,然後由解譯器執行。


這個中間碼或位元碼可以在任何平台上運行,使得 Java 成為一種平台無關的語言。






Java stack, heap

在 Java 中,Stack(堆疊)和 Heap(堆)是兩個不同的記憶體區域,用於存儲程式執行時的不同類型資料。


Stack(堆疊):

1. Stack 是一個有限的區域,用於存儲方法的執行上下文和本地變數。

2. 每個執行緒在運行時都有自己的 Stack。

3. Stack 中的資料是按照 Last-In-First-Out(後進先出)的順序操作的。

4. 堆疊的記憶體管理由 JVM 自動處理,不需要開發人員手動釋放記憶體。

5. 在方法調用時,方法的參數、局部變數和方法返回地址等資訊都存儲在 Stack 中。

6. 方法的堆疊框架(Stack Frame)被創建和銷毀,以支援方法的執行。

ps:方法返回地址是指當一個方法執行完畢後,程式需要知道要返回到哪個位置繼續執行。在方法調用期間,當遇到一個方法呼叫,程式會將該方法的返回地址(也稱為返回位址或返回目標)存儲在堆疊中。

例如以下程式碼
public class ReturnAddressExample { public static void main(String[] args) { int result = methodA(5, 7); System.out.println("Result: " + result); } public static int methodA(int a, int b) { int sum = a + b; int product = methodB(a, b); return sum + product; } public static int methodB(int x, int y) { int difference = x - y; return difference; } }


Heap(堆):

1. Heap 是一個動態分配的區域,用於存儲物件和數據結構。

2. 所有執行緒共享同一個 Heap,用於動態分配物件的記憶體。

3. Heap 中的資料可以隨時進行存取和操作。

4. 堆的記憶體管理由 Java 的垃圾回收器(Garbage Collector)負責,自動回收不再被使用的物件。

5. 在 Heap 中創建的物件具有較長的生命周期,直到不再被引用時才會被垃圾回收。

6. Java 物件、陣列和類的實例等都存儲在 Heap 中。


簡而言之,Stack 主要用於方法的執行上下文和本地變數,具有有限的大小,按照後進先出的順序操作。而 Heap 則用於動態分配物件的記憶體,具有較長的生命周期並由垃圾回收器進行管理。



Java 的 Stack 和 Heap 與作業系統的 Stack 和 Heap 一樣嗎?

Java 的 Stack 和 Heap 與作業系統的 Stack 和 Heap 在概念和用途上有些相似,但實際上存在一些重要的差異。


Stack(堆疊)的比較:

1. 概念上的相似性:Java 的 Stack 和作業系統的 Stack 都是後進先出(LIFO)的資料結構,用於存儲方法的執行上下文和本地變數等。

2. 差異點:

   - Java 的 Stack 是 JVM 在運行時用於管理方法調用和返回的記憶體區域,每個執行緒都有自己的 Stack。作業系統的 Stack 是作業系統提供的記憶體區域,用於處理函數呼叫、區域變數和返回地址等。

   - Java 的 Stack 是由 JVM 自動管理的,開發人員不需要直接操作或釋放記憶體。作業系統的 Stack 則是由作業系統管理的全域性的資源。


Heap(堆)的比較:

1. 概念上的相似性:Java 的 Heap 和作業系統的 Heap 都是用於動態分配和管理物件的記憶體區域。

2. 差異點:

   - Java 的 Heap 是 JVM 在運行時用於存儲物件和數據結構的記憶體區域,所有執行緒共享同一個 Heap。作業系統的 Heap 是作業系統提供的記憶體區域,用於存儲程序運行時的數據和動態分配的記憶體。

   - Java 的 Heap 是由 JVM 的垃圾回收器(Garbage Collector)自動管理的,負責回收不再被使用的物件。作業系統的 Heap 則是由作業系統管理的,負責分配和回收記憶體。


總結來說,Java 的 Stack 和 Heap 與作業系統的 Stack 和 Heap 在概念上有些相似,但在實際實現、管理和用途上存在重要的差異。Java 的 Stack 和 Heap 是 JVM 內部實現的記憶體區域,用於方法的執行和物件的動態分配,由 JVM 自動管理。作業系統的 Stack 和 Heap 則是作業系統提供的全域性記憶體區域,用於處理函數


呼叫、分配記憶體和管理程序的運行。


封裝,繼承,多型

 當談到物件導向程式設計中的封裝、繼承和多型時,可以使用現實生活中的動物類別作為例子來解釋。


1. 封裝(Encapsulation):

封裝是指將資料(屬性)和方法(行為)封裝在一個物件中,只對外部提供必要的接口以便存取和使用。舉例來說,假設我們有一個動物類別,稱為Animal。Animal類別中包含屬性(例如名字、年齡)和方法(例如發出聲音)。這些屬性和方法被封裝在Animal物件中,只有通過公開的介面(例如方法)才能存取和操作。


public class Animal { private String name; private int age; public Animal(String name, int age) { this.name = name; this.age = age; } public void makeSound() { // 實作聲音的方法 } }


2. 繼承(Inheritance):

繼承是指一個類別(稱為子類別)繼承另一個類別(稱為父類別)的屬性和方法。子類別可以繼承父類別的特性,同時可以添加新的屬性和方法,或者修改父類別的方法。舉例來說,我們可以建立一個Dog類別,它繼承Animal類別的屬性和方法,同時可以定義自己的特殊屬性(例如品種)和方法(例如尾巴搖動)。


public class Dog extends Animal { private String breed; public Dog(String name, int age, String breed) { super(name, age); this.breed = breed; } public void wagTail() { System.out.println(name + " is wagging its tail."); } @Override public void makeSound() { System.out.println(name + " says: Woof!"); } }


3. 多型(Polymorphism):

多型是指相同的方法名稱可以在不同的類別中具有不同的實現方式。這允許使用相同的介面來調用不同類別的方法,提供了更大的彈性。舉例來說,除了Dog類別,我們也可以建立一個Cat類別,它同樣繼承自Animal類別。雖然Dog和Cat都有make_sound方法,但它們的具體實現方式是不同的。


public class Cat extends Animal { public Cat(String name, int age) { super(name, age); } @Override public void makeSound() { System.out.println(name + " says: Meow!"); } }


現在,我們可以創建


不同的動物物件並調用它們的方法:


Animal dog = new Dog("Buddy", 3, "Labrador"); Animal cat = new Cat("Whiskers", 5); dog.makeSound(); // 輸出: Buddy says: Woof! cat.makeSound(); // 輸出: Whiskers says: Meow! ((Dog) dog).wagTail(); // 輸出: Buddy is wagging its tail.


這個例子展示了封裝、繼承和多型的概念在物件導向程式設計中的應用。封裝將屬性和方法封裝在物件中,繼承允許子類別繼承父類別的特性,並添加新的特性,而多型則使得不同的物件可以使用相同的介面來調用方法,但具體的實現方式可能不同。