重新認識 MSBuild - 1

来源:https://www.cnblogs.com/hez2010/archive/2022/05/19/a-brand-new-look-at-msbuild-1.html
-Advertisement-
Play Games

前言 很多人一談到 MSBuild,腦子裡就會出現 “XML”、“只能用 VS 的屬性框圖形界面操作”、“可定製性和擴展性差” 和 “性能低” 等印象,但實際上這些除了 “XML” 之外完全都是刻板印象:這些人用著 Visual Studio 提供的圖形界面,就完全不願意花個幾分鐘時間翻翻文檔去理解 ...


前言

很多人一談到 MSBuild,腦子裡就會出現 “XML”、“只能用 VS 的屬性框圖形界面操作”、“可定製性和擴展性差” 和 “性能低” 等印象,但實際上這些除了 “XML” 之外完全都是刻板印象:這些人用著 Visual Studio 提供的圖形界面,就完全不願意花個幾分鐘時間翻翻文檔去理解 MSBuild 及其構建過程。

另外,再加上 vcxproj (Visual C++ 項目)的預設 MSBuild 構建文件寫得確實談不上好(預設只能項目粒度並行編譯,想要源碼級並行編譯你得加錢),但這跟 MSBuild 本身沒有關係,單純是 Visual Studio 自帶的構建文件沒支持罷了。

實際上,MSBuild 是一個擴展性極強、開源、跨平臺且構建管道中都是傳遞的對象的構建系統,包含結構化信息處理和結構化日誌輸出的支持;另外,還提供了完整的 .NET Runtime 供你調用裡面任何的 API,甚至用 MSBuild 編程都不在話下。

本系列文章就來讓大家以新的視角重新認識一下 MSBuild,並藉助 MSBuild 來構建自己的項目。

安裝和使用

MSBuild 的開源代碼倉庫:https://github.com/dotnet/msbuild ,另外,MSBuild 也支撐了整個 .NET 的構建流程,因此安裝 MSBuild 最簡單的方法就是安裝一個 .NET SDK,同樣也是開源和跨平臺的。

安裝好後,你就可以通過運行 dotnet msbuild 調用 MSBuild 了。當然,你也可以選擇從源碼自行構建出一個 msbuild 可執行文件來用。

註意事項

在本系列文章中,將會編寫一個 build.proj 用來測試 MSBuild,並且本文中涉及到的 MSBuild 調用都是直接運行 msbuild 來完成的,如果你是用安裝 .NET SDK 的方法來安裝 MSBuild 的話,則需要使用 dotnet msbuild 來調用 MSBuild。

一些基礎

MSBuild 的構建文件中,主要分為以下幾個部分:

  • 項目(Project)
  • 屬性(Property)
  • 項(Item)
  • 任務(Task)
  • 目標(Target)
  • 導入(Import)

項目

項目是 MSBuild 構建文件的頂級節點。

<Project Sdk="...">
</Project>

可以用來引入 SDK 等元素,允許直接引用 SDK 中定義的構建文件,這個我們以後再具體說,目前只需要知道 Project 是 MSBuild 的頂層節點即可。

我們目前不需要引入什麼 SDK,因此新建一個 build.proj,在其中寫入以下代碼就行了:

<Project>
</Project>

屬性

屬性顧名思義,就是用來為 MSBuild 構建過程傳遞的參數,有多種方式可以定義屬性。

第一種方式是在構建的時候通過命令行參數 -property-p 傳入,例如:

msbuild build.proj -property:Configuration=Release

這樣就傳入了一個名為 Configuration 的屬性,它的值是 Release

還有一種方式是在構建文件中編寫:

<PropertyGroup>
  <Configuration>Release</Configuration>
</PropertyGroup>

PropertyGroup 就是專門用來編寫屬性的組,你可以在裡面利用 XML 來設置屬性。

對屬性的引用可以使用 $ 來引用,例如:

<Foo>hello</Foo>
<Bar>$(Foo) world!</Bar>

這樣 Bar 的值就會變成 hello world!。另外要註意,通過命令行傳入的屬性值優先順序比頂層 PropertyGroup 中定義的屬性更高,因此如果用戶調用了:

msbuild build.proj -property:Foo=goodbye

則此時 Bar 的值就變成了 goodbye world!

屬性的計算順序是從上到下計算的,並且屬性在 MSBuild 的構建過程中也是最先計算的。MSBuild 中也有一些內置屬性可以直接使用,例如 MSBuildProjectFullPath 表示當前項目的文件路徑等等,可以在 MSBuild 文檔中查閱。

項就是 MSBuild 構建過程中要用的集合對象了,你可以利用項來在 MSBuild 中定義你想使用的東西。

例如:

<ItemGroup>
  <Foo Include="hello" />
</ItemGroup>

這樣就定義了一個叫做 Foo 的項,它包含了一個 hello。其中,ItemGroup 是專門用來編寫項的組。

項之所以說是集合對象,因為它可以被理解為一個數組,你可以在構建文件中通過 IncludeUpdateRemove 來操作這個數組,Include 用來添加一個元素,Exclude 用來排除一個元素,Remove 用來刪除一個元素,Update 用來更新一個元素的元數據(metadata,至於元數據是什麼我們稍後再說),計算順序同樣也是從上到下的。

比如:

<ItemGroup>
  <Foo Include="1" />
  <Foo Include="2" />
  <Foo Include="3" />
  <Foo Include="4" />
  <Foo Remove="3" />
</ItemGroup>

就會得到一個項 Foo,它包含 1、2、4。

在 MSBuild 中,多個元素可以用 ; 分隔,因此也可以寫成:

<ItemGroup>
  <Foo Include="1;2;3;4" />
  <Foo Remove="3" />
</ItemGroup>

而 MSBuild 很貼心的為我們準備了一些通配符,用來快速添加項,例如 ***、和 ?,分別用來匹配一段路徑中的零個或多個字元、零段或多段路徑以及一個字元,然後配合 Exclude 可以篩選掉你不想要的東西。例如:

<ItemGroup>
  <Foo Include="**/*.cpp" Exclude="foo.cpp">
</ItemGroup>

就可以把當前目錄和子目錄中所有的 C++ 文件都添加到 Foo 項中,但是不包含 foo.cpp

什麼叫做元數據呢?例如我們如果想給 Foo 附帶一個數據 X,那麼可以這麼寫:

<ItemGroup>
  <Foo Include="1">
    <X>Hello</X>
  </Foo>
  <Foo Include="2">
    <X>World</X>
  </Foo>
</ItemGroup>

這樣 Foo 中的 1 就帶了一個值為 Hello 的 X,而 Foo 中的 2 則帶了一個值為 World 的 X,這個 X 就是項元素的元數據。

如果再加一個:

<Foo Update="1">
  <X>Goodbye</X>
</Foo>

則可以把 1 的 X 元數據更新為 Goodbye。

另外,我們可以通過 % 來從項上引用元數據,例如 %(Foo.X)

任務

任務是 MSBuild 真正要執行的東西,例如編譯、打包和下載文件等等任務,可以由我們自行用 C# 或者 VB.NET 等語言實現。

關於任務的編寫,我們將在以後進行介紹,這裡只簡單介紹一下任務的使用。

MSBuild 也內置了很多任務,例如 Message 用來列印信息、WarnError 分別用來產生警告和錯誤、CopyDelete 分別用來複制和刪除文件、 MakeDir 用來創建目錄、Exec 用來執行程式以及 DownloadFile 用來下載文件等等,具體的內置任務可以去 https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-task-reference 查看。

例如我們想要列印信息,那麼可以利用 Message 任務來完成,根據 Message 任務的文檔,我們知道它有 ImportanceText 兩個參數。

比如我們想要列印一下 Hello,就可以這麼寫:

<Message Text="Hello" />

任務需要在目標中使用。

目標

目標是一組任務的集合,我們簡單理解為:為了完成一個目標,需要執行一系列的任務。

例如:

<Target Name="Print">
  <Message Text="Hello" />
</Target>

這樣我們就定義了一個叫做 Print 的目標,它用來輸出一個 Hello

此時我們用 -target-t 指定執行 Print

msbuild build.proj -target:Print -verbosity:normal

將會輸出 Hello。註意 Message 的預設重要性為 normal,而 MSBuild 預設的日誌詳細等級為 quiet,只輸出 high 或以上優先順序的東西,因此我們指定 -verbosity:normal 讓 MSBuild 同樣把 normal 等級的日誌也輸出出來。

目標之間可以用過 BeforeTargetsAfterTargets 來設置順序(但是相互之間沒有依賴),還可以使用 DependsOnTargets 來設置依賴,例如:

<Target Name="PrintBye" DependsOnTargets="PrintHello;PrintWorld">
  <Message Text="Bye" Importance="high" />
</Target>

<Target Name="PrintHello">
  <Message Text="Hello" Importance="high" />
</Target>

<Target Name="PrintWorld" AfterTargets="PrintWorld">
  <Message Text="World" Importance="high" />
</Target>

執行構建 PrintBye

msbuild build.proj -target:PrintBye

將會輸出:

Hello
World
Bye

可以通過在項目上設置 DefaultTargets 表示如果沒有通過命令行參數傳入目標則預設執行的目標,還可以設置 InitialTargets 來表示始終最先執行的目標。

此外,目標還支持設置 InputsOutputsReturns,分別表示預計作為輸入的項、輸出的項和目標返回值,前兩個用於緩存和增量編譯,ReturnsOutputs 用法基本相同,但是 Returns 不參與增量編譯,關於增量編譯我們以後再介紹。

我們可以利用 CallTarget 任務來調用一個目標,然後獲取調用的目標的 Outputs 的輸出,通過這種方式,我們不需要手動編寫任務也能實現函數調用,例如:

<Target Name="Hello" Returns="$(Result)">
  <Message Text="你好,$(Name)" Importance="high" />
  <PropertyGroup>
    <Result>和 $(Name) 打了招呼</Result>
  </PropertyGroup>
</Target>

<Target Name="Build">
  <PropertyGroup>
    <MyResult>還沒調用結果</MyResult>
  </PropertyGroup>
  <Message Text="$(MyResult)" Importance="high" />
  <CallTarget Targets="Hello">
    <!-- 把 Hello 目標的輸出存到 MyResult 屬性中 -->
    <Output TaskParameter="TargetOutputs" PropertyName="MyResult"/>
  </CallTarget>
  <Message Text="$(MyResult)" Importance="high" />
</Target>

我們執行構建:

msbuild build.proj -target:Build -property:Name=Bob

得到輸出:

還沒調用結果
你好,Bob
和 Bob 打了招呼

這個時候你可能會想,如果要給這個 Hello 目標像函數調用那樣傳入參數怎麼辦?此時可以用 MSBuild 任務,通過 Properties 來傳遞屬性,多個屬性同樣是通過 ; 分隔,並且其中可以用 $@ 等引用其他屬性和項:

<Target Name="Hello" Returns="$(Result)">
  <Message Text="你好,$(Age) 歲的 $(Name)" Importance="high" />
  <PropertyGroup>
    <Result>和 $(Name) 打了招呼</Result>
  </PropertyGroup>
</Target>

<Target Name="Build">
  <PropertyGroup>
    <MyResult>還沒調用結果</MyResult>
  </PropertyGroup>
  <Message Text="$(MyResult)" Importance="high" />
  <MSBuild Targets="Hello" Properties="Age=18" Projects="build.proj">
    <Output TaskParameter="TargetOutputs" PropertyName="MyResult"/>
  </MSBuild>
  <Message Text="$(MyResult)" Importance="high" />
</Target>

然後我們傳入一個 Age 屬性進去來調用構建:

msbuild build.proj -target:Build -property:Name=Bob -p:Age=18

這次將會得到輸出:

還沒調用結果
你好,18 歲的 Bob
和 Bob 打了招呼

導入

導入顧名思義,就是導入其他的構建文件,這樣我們就可以不需要在一個文件中編寫所有的構建配置了。

導入很簡單,只需要在 Project 節點裡加入:

<Import Project="foo.proj" />

即可把引入的構建文件里的內容直接插入到所在的位置。

一點示例

截止到現在,我們已經瞭解了很多東西,那麼我們綜合起來用一下。

首先,創建一個 build.proj,裡面編寫:

<Project InitialTargets="PrintName" DefaultTargets="PrintInfo">
  <PropertyGroup>
    <Name>Alice</Name>
  </PropertyGroup>

  <ItemGroup>
    <Foo Include="1">
        <X>Hello</X>
    </Foo>
    <Foo Include="2">
        <X>World</X>
    </Foo>
  </ItemGroup>

  <Target Name="PrintName" >
    <Message Text="你好 $(Name)" Importance="high" />
  </Target>

  <Target Name="PrintInfo" DependsOnTargets="BeforePrint">
    <Message Text="@(Foo) 的元數據是 %(X)" Importance="high" />
  </Target>
  
  <Target Name="BeforePrint">
    <Message Text="即將輸出數據" Importance="high" />
  </Target>
  
  <Target Name="AfterPrint" AfterTargets="PrintInfo">
    <Message Text="數據已經輸出" Importance="high" />
  </Target>

</Project>

然後我們試著執行一下 MSBuild:

msbuild build.proj

將會輸出:

你好 Alice
即將輸出數據
1 的元數據是 Hello
2 的元數據是 World
數據已經輸出

條件

至此,你可能會覺得 MSBuild 里的一切都是線性的,然而要怎麼表達邏輯關係呢?這個時候就要用到條件(Condition)了。

我們可以在任何地方使用 Condition 來控制是否計算或執行一個屬性、項、任務、目標和引入,也就是說你可以在任何你想要的地方通過 Condition 來進行條件的控制。

例如:

<PropertyGroup>
  <Name>Alice</Name>
  <IsDefaultName Condition=" '$(Name)' == 'alice' ">true</IsDefaultName>
  <IsDefaultName Condition=" '$(Name)' != 'alice' ">false</IsDefaultName>
</PropertyGroup>

MSBuild 中允許我們進行字元串比較,並且預設是不區分大小寫的。上述代碼中,如果構建的時候 NameAlice,那麼 IsDefaultName 就是 true, 否則是 false

我們定義一個目標輸出一下看看:

<Target Name="Print">
  <Message Text="你好,$(Name),是否預設名字:$(IsDefaultName)" Importance="high" />
</Target>

運行:

msbuild build.proj

得到輸出

你好,Alice,是否預設名字:true

而如果我們通過命令行傳入一個 -property:Name=Bob,則輸出就變成了:

你好,Bob,是否預設名字:false

另外,我們還可以使用 ChooseWhenOtherwise 來根據 Condition 選擇 When 或者 Otherwise 下的內容,例如:

<Choose>
  <When Condition=" '$(Name)' == 'Alice' ">
    <PropertyGroup>
      <Age>16</Age>
    </PropertyGroup>
    <ItemGroup>
      <Files Include="Alice/**/*.*" />
    </ItemGroup>
  </When>
  <When Condition=" '$(Name)' == 'Bob' or '$(Name)' == 'David' ">
    <PropertyGroup>
      <Age>18</Age>
    </PropertyGroup>
    <ItemGroup>
      <Files Include="$(Name)/**/*.*" />
    </ItemGroup>
  </When>
  <Otherwise>
    <PropertyGroup>
      <Age>20</Age>
    </PropertyGroup>
    <ItemGroup>
      <Files Include="Other/**/*.*" />
    </ItemGroup>
  </Otherwise>
</Choose>

上面當 Name 是 Alice 的時候,將會選擇第一個 When 里的東西,而如果是 Bob 或者 David,則會選擇第二個 When 里的東西,否則選擇 Otherwise 里的東西。

條件將允許我們在構建過程中進行複雜的計算,並且控制整個構建流程。

任務錯誤處理

任務可能會發生錯誤,在 MSBuild 中,可以通過 Error 產生錯誤、Warn 產生警告;一些內置的任務(例如 DeleteCopy 等)也可能產生錯誤;對於自行編寫的任務而言,也有其方式產生錯誤或者警告。

如果發生了錯誤,則構建預設會直接停止並以失敗告終。但這不能滿足所有需要,因此我們還可以在任務上利用 ContinueOnError 來控制發生錯誤後的行為:

  • ErrorAndContinue:當任務失敗時繼續執行
  • WarnAndContinue 或 true:當任務失敗時繼續執行,並且把該任務中的錯誤視為警告
  • ErrorAndStop 或 false:當任務失敗時停止構建

例如,這次我們使用上面的例子,對非預設名字產生錯誤:

<Target Name="Print">
  <Message Text="你好,$(Name),是否預設名字:$(IsDefaultName)" Importance="high" />
  <Error Condition=" '$(IsDefaultName)' == 'false' " Text="發生錯誤了" />
</Target>

<Target Name="Build" DependsOnTargets="Print">
  <Message Text="構建完了" Importance="high" />
</Target>

此時執行構建:

msbuild build.proj -target:Build

將會輸出

你好,Alice,是否預設名字:true
構建完了

而如果執行:

dotnet msbuid build.proj -target:Build -property:Name=Bob

則會輸出

你好,Bob,是否預設名字:false
build.proj(10,5): error : 發生錯誤了

但如果我們把構建代碼改成:

<Target Name="Print">
  <Message Text="你好,$(Name),是否預設名字:$(IsDefaultName)" Importance="high" />
  <Error Condition=" '$(IsDefaultName)' == 'false' "  ContinueOnError="ErrorAndContinue" Text="發生錯誤了" />
</Target>

<Target Name="Build" DependsOnTargets="Print">
  <Message Text="構建完了" Importance="high" />
</Target>

再執行上述命令,則會輸出:

你好,Bob,是否預設名字:false
build.proj(10,5): error : 發生錯誤了
構建完了

MSBuild 和 .NET 函數調用

MSBuild 允許我們直接調用 MSBuild 內置的或者 .NET 中的函數,調用方法為 [類型名]::方法名(參數...),例如:

<PropertyGroup>
  <Foo>1</Foo>
  <Foo Condition="[MSBuild]::IsOsPlatform('Windows')">2</Foo>
</PropertyGroup>

Foo 在 Windows 上為 2,而在其他系統上為 1。

屬性和項都有各自的 MSBuild 內置函數可以用,例如 ExistsHasMetadata 等等,具體可在 MSBuild 官方文檔上查閱:

有了這些,我們便可以利用 MSBuild 完成各種事情。

結構化日誌

有時編寫好了構建文件之後,我們希望能夠查看整個構建流程或者失敗的原因等,這個時候文本的日誌就不夠用了。在 MSBuild 中,我們有強大的結構化日誌。

只需要構建的時候傳入一個 -bl 參數指定 binlog 的位置,MSBuild 就能在構建時為我們生成一個極其強大的結構化日誌,例如使用 “一點示例” 小節中的例子:

msbuild build.proj -bl:output.binlog

然後就可以在 MSBuild 結構化日誌查看器上查看我們的 output.binlog 了。這個查看器有網頁版和 Windows 客戶端版,因此無論在哪個平臺上都是可以用的:https://msbuildlog.com

利用 MSBuild 結構化日誌查看器,我們將能夠從頭到尾詳細掌控整個構建流程,包括屬性和項是怎麼計算出來的、目標為什麼被跳過了、目標的執行結果和執行時長是多少、有哪些目標依賴關係以及目標都是來自哪個構建文件的等等信息一覽無遺,這樣非常有助於我們快速編寫和診斷構建文件。

小總結

MSBuild 依托於 .NET 運行時,利用 XML 來描述構建文件,是一個無需守護進程(daemon)的非常強大的構建系統。

本文主要介紹了 MSBuild 的基本概念和編寫方法,以及結構化日誌的使用方法。

如果你厭煩了編寫 CMakeLists.txt、Makefile 的那種難以調試、文檔不全並且到處都是純字元串處理的體驗,不如試試 MSBuild,將能快速寫出可靠的構建配置,加速你的開發。

在下一篇文章中,我們將來介紹緩存、增量編譯、任務的編寫以及並行編譯等,讓 MSBuild 的構建變得又快又省心。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 來源:blog.csdn.net/qq_15371293/article/details/117090780 項目場景: ClickHouse 操作基於 Mybatis-puls源碼擴展開發。解決ClickHouse的修改和刪除 SQL操作與Mysql不相同。 基於 Mybatis-puls: up ...
  • 個人學習筆記總結。Basic Types、Strings、Arrays, Slices, and Maps、Control Statements、Declarations & Types、Formatted & File I/O、 Functions, Parameters、Defer、Closur... ...
  • #一、概述 ##1.1、Ribbon Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。簡單的說,Ribbon是Netflix發佈的開源項目,主要功能是提供客戶端的軟體負載均衡演算法和服務調用。Spring Cloud Ribbon雖然只是一個工具 ...
  • ##基本介紹 ###1.變數 定義:可以變化的量 ###2.變數聲明 Java是一種強制類型語言,每一個變數必須聲明類型 ###3.變數名,變數類型和作用域 Java變數是程式中最基本的存儲單元,其要素包括變數名,變數類型和作用域 type varName [=value] [{,varName[= ...
  • ​ 概述 服務編排是Fizz網關提供的一個強大的功能,能夠基於現有的業務微服務通過線上配置的方式快速的生成一個聚合介面,減少中間層膠水代碼以及降低編碼投入。在服務編排中支持使用函數,本進階教程中我們分三篇文章(上篇:列表展開&合併、中篇:列表提取&關聯、下篇:列表欄位重命名&欄位移除)來介紹數據列表 ...
  • 來源:zhihu.com/question/23084473 今天我們聊一個不常見的 Java 面試題:為什麼資料庫連接池不採用 IO 多路復用? 這是一個非常好的問題。IO多路復用被視為是非常好的性能助力器。但是一般我們在使用 DB 時,還是經常性採用c3p0,tomcat connection ...
  • 二刷jdbc 作者小結:從第一次大概幾天快速刷完jdbc,到如今的二刷,才發現自己對jdbc的理解有點太淺。到學習javaweb是創建資料庫層時的迷茫,到現在對這種設計模式的理解。我深有體會到了:實打實走好每一步的必要性!這篇筆記較為完整的展示了jdbc的發展脈絡,從原理到手動封裝,再到第三方庫,循 ...
  • #Redis簡介 Redis(Remote Dictionary Server)是完全開源的、遵守BSD協議的、高性能的Key-Value資料庫。 Redis與其他Key-Value緩存產品有一下三個特點: Redis支持數據的持久化,可以將記憶體中的數據保存在磁碟中,重啟的時候可以再次載入進行使用。 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文將以 C# 語言來實現一個簡單的布隆過濾器,為簡化說明,設計得很簡單,僅供學習使用。 感謝@時總百忙之中的指導。 布隆過濾器簡介 布隆過濾器(Bloom filter)是一種特殊的 Hash Table,能夠以較小的存儲空間較快地判斷出數據是否存在。常用於允許一定誤判率的數據過濾及防止緩存 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 「簡單有價值的事情長期堅持做」 這是成功最簡單,但也最難學的秘訣。不經過訓練,人很難意識到時間複利的威力。 仙劍奇俠傳的「十里坡劍神」和金庸群俠傳的「十級野球拳」,就是簡單的事情持之以恆反覆做,最後就有巨大的威力 唐家三少成為網文收入第一,最重要的一步是十四年從未斷日更 這樣的案例很多,一開始可能成 ...
  • 迎面走來了你的面試官,身穿格子衫,挺著啤酒肚,髮際線嚴重後移的中年男子。 手拿泡著枸杞的保溫杯,胳膊夾著MacBook,MacBook上還貼著公司標語:“我愛加班”。 面試開始,直入正題。 面試官: 看你簡歷上面寫著精通MySQL,我先問你事務的特性是什麼? 老生常談,這個還有誰不會背的嗎? 我: ...
  • 基礎知識 python是一門腳本語言,它是解釋執行的。 python使用縮進做為語法,而且python2環境下同一個py文件中不能同時存在tab和空格縮進,否則會出錯,建議在IDE中顯示縮進符。 python在聲明變數時不寫數據類型,可以type(xx)來獲取欄位的類型,然後可以int(),list ...
  • 為什麼要多線程下載 俗話說要以終為始,那麼我們首先要明確多線程下載的目標是什麼,不外乎是為了更快的下載文件。那麼問題來了,多線程下載文件相比於單線程是不是更快? 對於這個問題可以看下圖。 橫坐標是線程數,縱坐標是使用對應線程數下載對應文件時花費的時間,藍橙綠代表下載文件的大小,每個線程下載對應文件2 ...
  • 詳細講解python爬蟲代碼,爬微博搜索結果的博文數據。 爬取欄位: 頁碼、微博id、微博bid、微博作者、發佈時間、微博內容、轉發數、評論數、點贊數。 爬蟲技術: 1、requests 發送請求 2、datetime 時間格式轉換 3、jsonpath 快速解析json數據 4、re 正則表達式提... ...
  • 背景: 一般我們可以用HashMap做本地緩存,但是HashMap功能比較弱,不支持Key過期,不支持數據範圍查找等。故在此實現了一個簡易的本地緩存,取名叫fastmap。 功能: 1.支持數據過期 2.支持等值查找 3.支持範圍查找 4.支持key排序 實現思路: 1.等值查找採用HashMap2 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 本章是系列文章的第八章,用著色演算法進行寄存器的分配過程。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 寄存器分配 寄存器分配是為程式處理的值找到存儲位置的問題 這些值可以存放到寄存器,也可以存放在記憶體中 寄存器更快,但數量有限 記憶體很多,但 ...