文:宋瑞文
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
北部有線電視-提供穩定的寬頻光纖上網、高畫質HD數位頻道、第四台電視、數位電視,現在申辦免費體驗3個月"HD99高畫質套餐"
Linux 服務器我們天天打交道,特別是 Linux 工程師更是如此。為了保證服務器的安全與性能,我們經常需要監控服務器的一些狀態,以保證工作能順利開展。
本文介紹的幾個命令,不僅僅適用於服務器監控,也適用於我們日常情況下的開發。
watch 命令我們的使用頻率很高,它的基本作用是,按照指定頻率重複執行某一條指令。使用這個命令,我們可以重複調用一些命令來達到監控服務器的作用。
默認情況下,watch 命令的執行周期是 2 秒,但我們可以使用 -n 選項來指定運行頻率,比如我們想要每隔 5 秒執行 date 命令,可以這麼執行:
$ watch -n 5 date
一台服務器肯定有多人在用,特別是本部門的小夥伴。對於這些小夥伴有沒渾水摸魚,我們可以使用一些命令來監控他們。
我們可以每隔 10 秒執行 who 命令,來看看都有誰在使用服務器。
$ watch -n 10 who
Every 10.0s: who butterfly: Tue Jan 23 16:02:03 2019
shs :0 2019-01-23 09:45 (:0)
dory pts/0 2019-01-23 15:50 (192.168.0.5)
alvin pts/1 2019-01-23 16:01 (192.168.0.15)
shark pts/3 2019-01-23 11:11 (192.168.0.27)
如果發現系統運行很慢,我們可以調用 uptime 命令來查看系統平均負載情況。
$ watch uptime
Every 2.0s: uptime butterfly: Tue Jan 23 16:25:48 2019
16:25:48 up 22 days, 4:38, 3 users, load average: 1.15, 0.89, 1.02
一些關鍵的進程肯定不能掛,否則可能會影響到業務開展,所以我們可以重複統計服務器中的所有進程數量。
$ watch -n 5 'ps -ef | wc -l'
Every 5.0s: ps -ef | wc -l butterfly: Tue Jan 23 16:11:54 2019
245
想動態知道服務器內存使用情況,可以重複執行 free 命令。
$ watch -n 5 free -m
Every 5.0s: free -m butterfly: Tue Jan 23 16:34:09 2019
total used free shared buff/cache available
Mem: 5959 776 3276 12 1906 4878
Swap: 2047 0 2047
當然不僅僅是這些,我們還可以重複調用很多命令來對服務器一些關鍵參數進行監控,
使用 top 命令我們可以知道系統的很多關鍵參數,而且是動態更新的。默認情況下,top 監控的是系統的整體狀態,如果我們只想知道某個人的使用情況,可以使用 -u 選項來指定這個人。
$ top -u alvin
top - 16:14:33 up 2 days, 4:27, 3 users, load average: 0.00, 0.01, 0.02
Tasks: 199 total, 1 running, 198 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.2 sy, 0.0 ni, 99.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 5959.4 total, 3277.3 free, 776.4 used, 1905.8 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 4878.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
23026 alvin 20 0 46340 7820 6504 S 0.0 0.1 0:00.05 systemd
23033 alvin 20 0 149660 3140 72 S 0.0 0.1 0:00.00 (sd-pam)
23125 alvin 20 0 63396 5100 4092 S 0.0 0.1 0:00.00 sshd
23128 alvin 20 0 16836 5636 4284 S 0.0 0.1 0:00.03 zsh
在這個結果里,你不僅僅可以看到 alvin 這個用戶運行的所有的進程數,也可以看到每個進程所消耗的系統資源(CPU,內存),同時依然可以看到整個系統的關鍵參數。
如果你想知道每個用戶登錄服務器所使用的時間,你可以使用 ac 命令。這個命令需要你安裝 acct 包(Debian)或 psacct 包(RHEL,Centos)。
如果我們想知道所有用戶登陸服務器所使用的時間之和,我們可以直接運行 ac 命令,無需任何參數。
$ ac
total 1261.72
如果我們想知道各個用戶所使用時間,可以加上 -p 選項。
$ ac -p
shark 5.24
alvin 5.52
shs 1251.00
total 1261.76
我們還可以通過加上 -d 選項來查看具體每一天用戶使用服務器時間之和。
$ ac -d | tail -10
Jan 11 total 0.05
Jan 12 total 1.36
Jan 13 total 16.39
Jan 15 total 55.33
Jan 16 total 38.02
Jan 17 total 28.51
Jan 19 total 48.66
Jan 20 total 1.37
Jan 22 total 23.48
Today total 9.83
我們可以使用很多命令來監控系統的運行狀態,本文主要介紹了三個:watch 命令可以讓你重複執行某一條命令來監控一些參數的變化,top 命令可以查看某個用戶運行的進程數以及消耗的資源,而 ac 命令則可以查看每個用戶使用服務器時間。你經常使用哪個命令呢?歡迎留言討論!
公眾號:良許Linux
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
文章每周持續更新,原創不易,「三連」讓更多人看到是對我最大的肯定。可以微信搜索公眾號「 後端技術學堂 」第一時間閱讀(一般比博客早更新一到兩篇)
”
對於一般的語言使用者來說 ,20% 的語言特性就能夠滿足 80% 的使用需求,剩下在使用中掌握。基於這一理論,Go 基礎系列的文章不會刻意追求面面俱到,但該有知識點都會覆蓋,目的是帶你快跑趕上 Golang 這趟新車。
Hurry up , Let’s go !
控制語句是程序的靈魂,有了它們程序才能完成各種邏輯,今天我們就來學習 Go 中的各種控制語句。
通過本文的學習你將掌握以下知識:
與大多數編程語言一樣,if 用於條件判斷,當條件表達式 expr 為 true 執行 {} 包裹的消息體語句,否則不執行。
語法是這樣的:
if expr {
// some code
}
**注意:**語法上和 c 語言不同的是不用在條件表達式 expr 外帶括號,和 python 的語法類似。
當然,如果想在條件不滿足的時候做點啥,就可以 if 后帶 else 語句。語法:
if expr {
// some code
} else {
// another code
}
除了可以在 if 中做條件判斷之外,在 Golang 中你甚至可以在 if 的條件表達式前執行一個簡單的語句。
舉個例子:
if x2 := 1; x2 > 10 {
fmt.Println("x2 great than 10")
} else {
fmt.Println("x2 less than 10", x2)
}
上面的例子在 if 語句中先聲明並賦值了 x2,之後對 x2 做條件判斷。
注意:此處在 if 內聲明的變量 x2 作用域僅限於 if 和else 語句。
當需要重複執行的時候需要用到循環語句,Go 中只有 for 這一種循環語句。
標準的for循環語法:
for 初始化語句; 條件表達式; 後置語句 {
// some code
}
這種語法形式和 C 語言中 for 循環寫法還是很像的,不同的是不用把這三個部分用 () 括起來。循環執行邏輯:
true 則執行循環體內語句,否則不執行。舉個例子:
sum := 0
for i := 0; i < 10; i++ {
sum += i // i作用域只在for語句內
fmt.Println(i, sum)
}
注意:循環變量i 的作用域只在 for 語句內,超出這個範圍就不能使用了。
前面說了,Golang 中只有 for 這一種循環語法,那有沒有類似 C 語言中 while 循環的寫法呢?答案是有的:把 for 語句的前後兩部分省略,只留中間的「條件表達式」的 for 語句等價於 while 循環。
像下面這樣:
sum1 := 0
for ;sum1 < 10; { // 可以省略初始化語句和後置語句
sum1++
fmt.Println(sum1)
}
上面的示例沒有初始化語句和後置語句,會循環執行 10 次後退出。
當然你要是覺得前後的分號也不想寫了,也可以省略不寫,上面的代碼和下面是等效的:
sum1 := 0
for sum1 < 10 { // 可以省略初始化語句和後置語句,分號也能省略
sum1++
fmt.Println(sum1)
}
在 Golang 中死循環可以這樣寫,相當於 C 語言中的 while(true)
for { // 死循環
// your code
}
switch 語句可以簡化多個 if-else 條件判斷寫法,避免代碼看起來雜亂。
可以先定義變量,然後在 switch 中使用這個變量。
a := 1
switch a {
case 1:
fmt.Println("case 1") // 不用寫break 執行到這自動跳出
case 2:
fmt.Println("case 2")
default:
fmt.Printf("unexpect case")
}
輸出:case 1
從 C 語言過來的朋友一定有這樣的經歷:經常會在 case 語句中漏掉 break 導致程序繼續往下執行,從而產生奇奇怪怪的 bug ,這種問題在 Golang 中不復存在了。
Golang 在每個 case 後面隱式提供 break 語句。 除非以 fallthrough 語句結束,否則分支會自動終止。
switch a := 1; a { //這裡有分號
case 1: // case 無需為常量,且取值不必為整數。
fmt.Println("case 1") // 不用寫break 執行到自動跳出 除非以 fallthrough 語句結束
fallthrough
case 2:
fmt.Println("case 2")
default:
fmt.Printf("unexpect case")
}
輸出:
case 1
case 2
還可以直接在 switch 中定義變量后使用,但是要注意變量定義之後又分號,比如下面這樣:
switch b :=1; b { //注意這裡有分號
case 1:
fmt.Println("case 1")
case 2:
fmt.Println("case 2")
default:
fmt.Printf("unexpect case")
}
沒有條件的 switch 同 switch true 一樣,只有當 case 中的表達式值為「真」時才執行,這種形式能簡化複雜的 if-else-if else 語法。
下面是用 if 來寫多重條件判斷,這裏寫的比較簡單若是再多幾個 else if 代碼結構看起來會更糟糕。
a := 1
if a > 0 {
fmt.Println("case 1")
} else if a < 0 {
fmt.Println("case 2")
} else {
fmt.Printf("unexpect case")
}
如果用上不帶條件的 switch 語句,寫出來就會簡潔很多,像下面這樣。
a := 1
switch { // 相當於switch true
case a > 0: // 若表達式為「真」則執行
fmt.Println("case 1")
case a < 0:
fmt.Println("case 2")
default:
fmt.Printf("unexpect case")
}
defer 語句有延遲調用的效果。具體來說defer後面的函數調用會被壓入堆棧,當外層函數返回才會對壓棧的函數按後進先出順序調用。說起來有點抽象,舉個例子:
package main
import "fmt"
func main() {
fmt.Println("entry main")
for i := 0; i < 6; i++ {
defer fmt.Println(i)
}
fmt.Println("exit main")
}
fmt.Println(i) 不會每次立即執行,而是在 main 函數返回之後才依次調用,編譯運行上述程序的輸出:
entry main
exit main //外層函數返回
5
4
3
2
1
0
上面是簡單的使用示例,實際使用中defer 通常用來釋放函數內部變量,因為它可以在外層函數 return 之後繼續執行一些清理動作。
這在文件類操作異常處理中非常實用,比如用於釋放文件描述符,我們以後會講解這塊應用,總之先記住 defer 延遲調用的特點。
通過本文的學習,我們掌握了 Golang 中基本的控制流語句,利用這些控制語句加上一節介紹的變量等基礎知識,可以構成豐富的程序邏輯,就能用 Golang 來做一些有意思的事情了。
感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反覆求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,我們一起在探討中學習.
今天的技術分享就到這裏,我們下期再見。
創作不易,白票不是好習慣,如果在我這有收穫,動動手指「點贊」「關注」是對我持續創作的最大支持。
微信搜索公眾號「 後端技術學堂 」回復「資料」「1024」有我給你準備的各種編程學習資料。文章每周持續更新,我們下期見!
”
本文使用 mdnice 排版
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
首先我們來看一段控制台應用代碼:
class Program
{
static async Task Main(string[] args)
{
System.Console.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
var result = await ExampleTask(2);
System.Console.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
System.Console.WriteLine(result);
Console.WriteLine("Async Completed");
}
private static async Task<string> ExampleTask(int Second)
{
await Task.Delay(TimeSpan.FromSeconds(Second));
return $"It's Async Completed in {Second} seconds";
}
}
輸出結果
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:4,Is Thread Pool:True
It's Async Completed in 2 seconds
Async Completed
如果這段代碼在WPF運行,猜猜會輸出啥?
private async void Async_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
var result= await ExampleTask(2);
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
Debug.WriteLine(result);
Debug.WriteLine("Async Completed");
}
private async Task<string> ExampleTask(int Second)
{
await Task.Delay(TimeSpan.FromSeconds(Second));
return $"It's Async Completed in {Second} seconds";
}
輸出結果:
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:1,Is Thread Pool:False
It's Async Completed in 2 seconds
Async Completed
這時候你肯定是想說,小朋友,你是否有很多問號????,我們接下看下去
首先我們知道async await 異步函數本質是狀態機,我們通過反編譯工具dnspy,看看反編譯的兩段代碼是否有不同之處:
控制台應用:
internal class Program
{
[DebuggerStepThrough]
private static Task Main(string[] args)
{
Program.<Main>d__0 <Main>d__ = new Program.<Main>d__0();
<Main>d__.args = args;
<Main>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<Main>d__.<>1__state = -1;
<Main>d__.<>t__builder.Start<Program.<Main>d__0>(ref <Main>d__);
return <Main>d__.<>t__builder.Task;
}
[DebuggerStepThrough]
private static Task<string> ExampleTask(int Second)
{
Program.<ExampleTask>d__1 <ExampleTask>d__ = new Program.<ExampleTask>d__1();
<ExampleTask>d__.Second = Second;
<ExampleTask>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
<ExampleTask>d__.<>1__state = -1;
<ExampleTask>d__.<>t__builder.Start<Program.<ExampleTask>d__1>(ref <ExampleTask>d__);
return <ExampleTask>d__.<>t__builder.Task;
}
[DebuggerStepThrough]
private static void <Main>(string[] args)
{
Program.Main(args).GetAwaiter().GetResult();
}
}
WPF:
public class MainWindow : Window, IComponentConnector
{
public MainWindow()
{
this.InitializeComponent();
}
[DebuggerStepThrough]
private void Async_Click(object sender, RoutedEventArgs e)
{
MainWindow.<Async_Click>d__1 <Async_Click>d__ = new MainWindow.<Async_Click>d__1();
<Async_Click>d__.<>4__this = this;
<Async_Click>d__.sender = sender;
<Async_Click>d__.e = e;
<Async_Click>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
<Async_Click>d__.<>1__state = -1;
<Async_Click>d__.<>t__builder.Start<MainWindow.<Async_Click>d__1>(ref <Async_Click>d__);
}
[DebuggerStepThrough]
private Task<string> ExampleTask(int Second)
{
MainWindow.<ExampleTask>d__3 <ExampleTask>d__ = new MainWindow.<ExampleTask>d__3();
<ExampleTask>d__.<>4__this = this;
<ExampleTask>d__.Second = Second;
<ExampleTask>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
<ExampleTask>d__.<>1__state = -1;
<ExampleTask>d__.<>t__builder.Start<MainWindow.<ExampleTask>d__3>(ref <ExampleTask>d__);
return <ExampleTask>d__.<>t__builder.Task;
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "4.8.1.0")]
public void InitializeComponent()
{
bool contentLoaded = this._contentLoaded;
if (!contentLoaded)
{
this._contentLoaded = true;
Uri resourceLocater = new Uri("/WpfApp1;component/mainwindow.xaml", UriKind.Relative);
Application.LoadComponent(this, resourceLocater);
}
}
private bool _contentLoaded;
}
我們可以看到完全是一致的,沒有任何區別,為什麼編譯器生成的代碼是一致的,卻會產生不一樣的結果,我們看看創建和啟動狀態機代碼部分的實現:
public static AsyncVoidMethodBuilder Create()
{
SynchronizationContext synchronizationContext = SynchronizationContext.Current;
if (synchronizationContext != null)
{
synchronizationContext.OperationStarted();
}
return new AsyncVoidMethodBuilder
{
_synchronizationContext = synchronizationContext
};
}
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<[Nullable(0)] TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
AsyncMethodBuilderCore.Start<TStateMachine>(ref stateMachine);
}
[DebuggerStepThrough]
public static void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
if (stateMachine == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
}
Thread currentThread = Thread.CurrentThread;
Thread thread = currentThread;
ExecutionContext executionContext = currentThread._executionContext;
ExecutionContext executionContext2 = executionContext;
SynchronizationContext synchronizationContext = currentThread._synchronizationContext;
try
{
stateMachine.MoveNext();//狀態機執行代碼
}
finally
{
SynchronizationContext synchronizationContext2 = synchronizationContext;
Thread thread2 = thread;
if (synchronizationContext2 != thread2._synchronizationContext)
{
thread2._synchronizationContext = synchronizationContext2;
}
ExecutionContext executionContext3 = executionContext2;
ExecutionContext executionContext4 = thread2._executionContext;
if (executionContext3 != executionContext4)
{
ExecutionContext.RestoreChangedContextToThread(thread2, executionContext3, executionContext4);
}
}
}
在這裏總結下:
同樣的這裏貌似沒能獲取到原因,但是有個很關鍵的地方,就是Create函數為啥要獲取當前同步執行上下文,之後我從MSDN找到關於SynchronizationContext
的介紹,有興趣的朋友可以去閱讀以下,以下是各個.NET框架使用的SynchronizationContext:
| SynchronizationContext | 默認 |
|---|---|
| WindowsFormsSynchronizationContext | WindowsForm |
| DispatcherSynchronizationContext | WPF/Silverlight |
| AspNetSynchronizationContext | ASP.NET |
我們貌似已經一步步接近真相了,接下來我們來看看DispatcherSynchronizationContext
首先來看看DispatcherSynchronizationContext類的比較關鍵的幾個函數實現:
public DispatcherSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority)
{
if (dispatcher == null)
{
throw new ArgumentNullException("dispatcher");
}
Dispatcher.ValidatePriority(priority, "priority");
_dispatcher = dispatcher;
_priority = priority;
SetWaitNotificationRequired();
}
//同步執行
public override void Send(SendOrPostCallback d, object state)
{
if (BaseCompatibilityPreferences.GetInlineDispatcherSynchronizationContextSend() && _dispatcher.CheckAccess())
{
_dispatcher.Invoke(DispatcherPriority.Send, d, state);
}
else
{
_dispatcher.Invoke(_priority, d, state);
}
}
//異步執行
public override void Post(SendOrPostCallback d, object state)
{
_dispatcher.BeginInvoke(_priority, d, state);
}
我們貌似看到了熟悉的東西了,Send函數調用Dispatcher的Invoke函數,Post函數調用Dispatcher的BeginInvoke函數,那麼是否WPF執行異步函數之後會調用這裏的函數嗎?我用dnspy進行了調試:
我通過調試之後發現,當等待執行完整個狀態機的之後,也就是兩秒后跳轉到該Post函數,那麼,我們可以將之前的WPF那段代碼大概可以改寫成如此:
private async void Async_Click(object sender, RoutedEventArgs e)
{
//async生成狀態機的Create函數。獲取到UI主線程的同步執行上下文
DispatcherSynchronizationContext synchronizationContext = (DispatcherSynchronizationContext)SynchronizationContext.Current;
//UI主線程執行
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
//開始在狀態機的MoveNext執行該異步操作
var result= await ExampleTask(2);
//等待兩秒,異步執行完成,再在同步上下文異步執行
synchronizationContext.Post((state) =>
{
//模仿_dispatcher.BeginInvoke
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
Debug.WriteLine(result);
Debug.WriteLine("Async Completed");
},"Post");
}
輸出結果:
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:1,Is Thread Pool:False
It's Async Completed in 2 seconds
Async Completed
也就是asyn負責生成狀態機和執行狀態機,await將代碼分為兩部分,一部分是異步執行狀態機部分,一部分是異步執行完之後,通過之前拿到的DispatcherSynchronizationContext,再去異步執行接下來的部分。我們可以通過dnspy調試DispatcherSynchronizationContext的 _dispatcher字段的Thread屬性,知道Thread為UI主線程,而同步界面UI控件的時候,也就是通過Dispatcher的BeginInvoke函數去執行同步的
Task有個ConfigureAwait方法,是可以設置是否對Task的awaiter的延續任務執行原始上下文,也就是為true時,是以一開始那個UI主線程的DispatcherSynchronizationContext執行Post方法,而為false,則以await那個Task裏面的DispatcherSynchronizationContext執行Post方法,我們來驗證下:
我們將代碼改為以下:
private async void Async_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
var result= await ExampleTask(2).ConfigureAwait(false);
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
Debug.WriteLine(result);
Debug.WriteLine($"Async Completed");
}
輸出:
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:4,Is Thread Pool:True
It's Async Completed in 2 seconds
Async Completed
結果和控制台輸出的一模一樣,且通過dnspy斷點調試依舊進入到DispatcherSynchronizationContext的Post方法,因此我們也可以證明我們上面的猜想,而且默認ConfigureAwait的參數是為true的,我們還可以將異步結果賦值給UI界面的Text block:
private async void Async_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
var result= await ExampleTask(2).ConfigureAwait(false);
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
this.txt.Text = result;//修改部分
Debug.WriteLine($"Async Completed");
}
拋出異常:
調用線程無法訪問此對象,因為另一個線程擁有該對象
補充
推薦林大佬的一篇文章,也講的也簡潔透徹C# dotnet 自己實現一個線程同步上下文
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
這個階段會給cute-dl添加循環層,使之能夠支持RNN–循環神經網絡. 具體目標包括:
RNN模型用來捕捉序列數據的特徵. 給定一個長度為T的輸入系列\(X=(x_1, x_2, .., X_T)\), RNN層輸出一個長度為T的序列\(H=(h_1, h_2, …, H_T)\), 對於任意時間步t, 可以表示為:
\[H_t = δ(X_tW_x + H_{t-1}W_h + b), \quad t = 2, 3, .., T \]
函數δ是sigmoid函數:
\[δ = \frac{1}{1 + e^{-x}} \]
\(H_t\)包含了前面第1到t-1步的所有信息。 和CNN層類似, CNN層在空間上共享參數, RNN層在時間步上共享參數\(W_x, W_h, b\).
RNN層中隱藏層的數量為T-2, 如果T較大(超過10), 反向傳播是很容易出現梯度爆炸. GRU和LSTM就是為了解決這個問題而誕生, 這兩種模型,可以讓RNN能夠支持長度超過1000的輸入序列。
GRU使用了不同功能的門控單元, 分別捕捉序列上不同時間跨度的的依賴關係。每個門控單元都會都有獨立的參數, 這些參數在時間步上共享。
GRU的門控單元有:
\(R_t = δ(X_tW^r_x + H_{t-1}W^r_h + b^r)\), 重置門用於捕捉短期依賴關係.
\(U_t = δ(X_tW^u_x + H_{t-1}W^u_h + b^u)\), 更新門用於捕捉長期依賴關係
\(\bar{H}_t = tanh(X_t\bar{W}_x + (R_t * H_{t-1})\bar{W}_h + \bar{b})\)
除此之外, 還有一個輸出單元:
\(H_t = U_t * H_{t-1} + (1-U_t)*\bar{H}_t\)
LSTM的設計思路和GRU類似, 同樣使用了多個門控單元:
\(I_t = δ(X_tW^i_x + H_{t-1}W^i_h + b^i)\), 輸入門,過濾記憶門的輸出.
\(F_t = δ(X_tW^f_x + H_{t-1}W^f_h + b^f)\), 遺忘門, 過濾前面時間步的記憶.
\(O_t = δ(X_tW^o_x + H_{t-1}W^o_h + b^o)\), 輸出門, 過濾當前時間步的記憶.
\(M_t = tanh(X_tW^m_x + H_{t-1}W^m_h + b^m)\), 記憶門.
它還有自己獨有的記憶單元和輸出單元:
\(\bar{M}_t = F_t * \bar{M}_{t-1} + I_t * M_t\)
\(H_t = O_t * tanh(\bar{M}_t)\)
設計要求:
文件: cutedl/rnn_layers.py, 類名: RNN
這個類是RNN層基類, 它主要功能是控制向前傳播和向後傳播的主流程.
初始化參數:
'''
out_units 輸出單元數
in_units 輸入單元數
stateful 保留當前批次的最後一個時間步的狀態作為下一個批次的輸入狀態, 默認False不保留
RNN 的輸入形狀是(m, t, in_units)
m: batch_size
t: 輸入系列的長度
in_units: 輸入單元數頁是輸入向量的維數
輸出形狀是(m, t, out_units)
'''
def __init__(self, out_units, in_units=None, stateful=False, activation='linear'):
向前傳播
def forward(self, in_batch, training):
m, T, n = in_batch.shape
out_units = self.__out_units
#所有時間步的輸出
hstatus = np.zeros((m, T, out_units))
#上一步的輸出
pre_hs = self.__pre_hs
if pre_hs is None:
pre_hs = np.zeros((m, out_units))
#隱藏層循環過程, 沿時間步執行
for t in range(T):
hstatus[:, t, :] = self.hiden_forward(in_batch[:,t,:], pre_hs, training)
pre_hs = hstatus[:, t, :]
self.__pre_hs = pre_hs
#pdb.set_trace()
if not self.stateful:
self.__pre_hs = None
return hstatus
反向傳播
def backward(self, gradient):
m, T, n = gradient.shape
in_units = self.__in_units
grad_x = np.zeros((m, T, in_units))
#pdb.set_trace()
#從最後一個梯度開始反向執行.
for t in range(T-1, -1, -1):
grad_x[:,t,:], grad_hs = self.hiden_backward(gradient[:,t,:])
#pdb.set_trace()
if t - 1 >= 0:
gradient[:,t-1,:] = gradient[:,t-1,:] + grad_hs
#pdb.set_trace()
return grad_x
\[sigmoid = \frac{1}{1+e^{-x}} \]
\[\frac{d}{dx}sigmoid = sigmoid(1-sigmoid) \]
\[tanh = \frac{e^x – e^{-x}}{e^x + e^{-x}} \]
\[\frac{d}{dx}tanh = 1 – tanh^2 \]
文件: cutedl/rnn_layers.py, 類名: GateUint
門控單元是RNN層基礎的參數單元. 和Dense層類似,它是Layer的子類,負責學習和使用參數。但在學習和使用參數的方式上有很大的不同:
下面我們會主要看一下GateUnit特別之處的代碼.
在__ init__方法中定義參數和棧:
#3個參數
self.__W = None #當前時間步in_batch權重參數
self.__Wh = None #上一步輸出的權重參數
self.__b = None #偏置量參數
#輸入棧
self.__hs = [] #上一步輸出
self.__in_batchs = [] #當前時間步的in_batch
正向傳播:
def forward(self, in_batch, hs, training):
W = self.__W.value
b = self.__b.value
Wh = self.__Wh.value
out = in_batch @ W + hs @ Wh + b
if training:
#向前傳播訓練時把上一個時間步的輸出和當前時間步的in_batch壓棧
self.__hs.append(hs)
self.__in_batchs.append(in_batch)
#確保反向傳播開始時參數的梯度為空
self.__W.gradient = None
self.__Wh.gradient = None
self.__b.gradient = None
return self.activation(out)
反向傳播:
def backward(self, gradient):
grad = self.activation.grad(gradient)
W = self.__W.value
Wh = self.__Wh.value
pre_hs = self.__hs.pop()
in_batch = self.__in_batchs.pop()
grad_in_batch = grad @ W.T
grad_W = in_batch.T @ grad
grad_hs = grad @ Wh.T
grad_Wh = pre_hs.T @ grad
grad_b = grad.sum(axis=0)
#反向傳播計算
if self.__W.gradient is None:
#當前批次第一次
self.__W.gradient = grad_W
else:
#累積當前批次的所有梯度
self.__W.gradient = self.__W.gradient + grad_W
if self.__Wh.gradient is None:
self.__Wh.gradient = grad_Wh
else:
self.__Wh.gradient = self.__Wh.gradient + grad_Wh
if self.__b.gradient is None:
self.__b.gradient = grad_b
else:
self.__b.gradient = self.__b.gradient + grad_b
return grad_in_batch, grad_hs
文件: cutedl/rnn_layers.py, 類名: GRU
隱藏單初始化:
def set_parent(self, parent):
super().set_parent(parent)
out_units = self.out_units
in_units = self.in_units
#pdb.set_trace()
#重置門
self.__g_reset = GateUnit(out_units, in_units)
#更新門
self.__g_update = GateUnit(out_units, in_units)
#候選輸出門
self.__g_cddout = GateUnit(out_units, in_units, activation='tanh')
self.__g_reset.set_parent(self)
self.__g_update.set_parent(self)
self.__g_cddout.set_parent(self)
#重置門乘法單元
self.__u_gr = MultiplyUnit()
#輸出單元
self.__u_out = GRUOutUnit()
向前傳播:
def hiden_forward(self, in_batch, pre_hs, training):
gr = self.__g_reset.forward(in_batch, pre_hs, training)
gu = self.__g_update.forward(in_batch, pre_hs, training)
ugr = self.__u_gr.forward(gr, pre_hs, training)
cddo = self.__g_cddout.forward(in_batch, ugr, training)
hs = self.__u_out.forward(gu, pre_hs, cddo, training)
return hs
反向傳播:
def hiden_backward(self, gradient):
grad_gu, grad_pre_hs, grad_cddo = self.__u_out.backward(gradient)
#pdb.set_trace()
grad_in_batch, grad_ugr = self.__g_cddout.backward(grad_cddo)
#計算梯度的過程中需要累積上一層輸出的梯度
grad_gr, g_pre_hs = self.__u_gr.backward(grad_ugr)
grad_pre_hs = grad_pre_hs + g_pre_hs
g_in_batch, g_pre_hs = self.__g_update.backward(grad_gu)
grad_in_batch = grad_in_batch + g_in_batch
grad_pre_hs = grad_pre_hs + g_pre_hs
g_in_batch, g_pre_hs = self.__g_reset.backward(grad_gr)
grad_in_batch = grad_in_batch + g_in_batch
grad_pre_hs = grad_pre_hs + g_pre_hs
#pdb.set_trace()
return grad_in_batch, grad_pre_hs
文件: cutedl/rnn_layers.py, 類名: LSTM
隱藏單元初始化:
def set_parent(self, layer):
super().set_parent(layer)
in_units = self.in_units
out_units = self.out_units
#輸入門
self.__g_in = GateUnit(out_units, in_units)
#遺忘門
self.__g_forget = GateUnit(out_units, in_units)
#輸出門
self.__g_out = GateUnit(out_units, in_units)
#記憶門
self.__g_memory = GateUnit(out_units, in_units, activation='tanh')
self.__g_in.set_parent(self)
self.__g_forget.set_parent(self)
self.__g_out.set_parent(self)
self.__g_memory.set_parent(self)
#記憶單元
self.__memory_unit =LSTMMemoryUnit()
#輸出單元
self.__out_unit = LSTMOutUnit()
向前傳播:
def hiden_forward(self, in_batch, hs, training):
g_in = self.__g_in.forward(in_batch, hs, training)
#pdb.set_trace()
g_forget = self.__g_forget.forward(in_batch, hs, training)
g_out = self.__g_out.forward(in_batch, hs, training)
g_memory = self.__g_memory.forward(in_batch, hs, training)
memory = self.__memory_unit.forward(g_forget, g_in, g_memory, training)
cur_hs = self.__out_unit.forward(g_out, memory, training)
return cur_hs
反向傳播:
def hiden_backward(self, gradient):
#pdb.set_trace()
grad_out, grad_memory = self.__out_unit.backward(gradient)
grad_forget, grad_in, grad_gm = self.__memory_unit.backward(grad_memory)
grad_in_batch, grad_hs = self.__g_memory.backward(grad_gm)
tmp1, tmp2 = self.__g_out.backward(grad_out)
grad_in_batch += tmp1
grad_hs += tmp2
tmp1, tmp2 = self.__g_forget.backward(grad_forget)
grad_in_batch += tmp1
grad_hs += tmp2
tmp1, tmp2 = self.__g_in.backward(grad_in)
grad_in_batch += tmp1
grad_hs += tmp2
return grad_in_batch, grad_hs
接下來, 驗證示例將會構建一個簡單的RNN模型, 使用該模型擬合一個正餘弦疊加函數:
#採樣函數
def sample_function(x):
y = 3*np.sin(2 * x * np.pi) + np.cos(x * np.pi) + np.random.uniform(-0.05,0.05,len(x))
return y
訓練數據集和測試數據集在這個函數的不同定義域區間內樣. 訓練數據集的採樣區間為[1, 200.01), 測試數據集的採樣區間為[200.02, 240.002). 模型任務是預測這個函數值的序列.
示例代碼在examples/rnn/fit_function.py文件中.
def fit_gru():
model = Model([
rnn.GRU(32, 1),
nn.Filter(),
nn.Dense(32),
nn.Dense(1, activation='linear')
])
model.assemble()
fit('gru', model)
訓練報告:
def fit_lstm():
model = Model([
rnn.LSTM(32, 1),
nn.Filter(),
nn.Dense(2),
nn.Dense(1, activation='linear')
])
model.assemble()
fit('lstm', model)
訓練報告:
這個階段,框架新增了RNN的兩個最常見的實現:GRU和LSTM, 相應地增加了它需要的激活函數. cute-dl已經具備了構建最基礎RNN模型的能力。通過驗證發現, GRU模型和LSTM模型在簡單任務上都表現出了很好的性能。會添加嵌入層,使框架能夠構建文本分類任務的模型,然後在imdb-review(電影評價)數據集上進行驗證.
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
摘錄自2020年5月27日中央社報導
「每日晨報」(Daily Sabah)報導,博斯普魯斯海峽(Bosporus)水色自26日起轉變成「土耳其藍」。伊斯坦堡科技大學(Istanbul Technical University)教授托羅斯(Huseyin Toros)認為,東北風是導致「海水變色」主要原因。
托羅斯指出:「單細胞生物被東北風曳引進入博斯普魯斯海峽,海水表面經過折射,轉變成土耳其藍色。在此一大氣環境下的氣流、海平面下的活動、不同微生物、白天陽光變化等因素也可能導致海水顏色產生變化。」他表示,海水將於幾天內恢復「本色」。
美國國家航空暨太空總署(NASA)的衛星於當年5月29日首度補捉到黑海浮游生物激增的圖像。漁夫們相信,海中出現大量浮游生物意味當年鯷魚產量將會大增。但是浮游生物也會消耗水中大量氧氣,從而對其他海洋生物造成傷害。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
《SpringBoot-2.3容器化技術》系列,旨在和大家一起學習實踐2.3版本帶來的最新容器化技術,讓咱們的Java應用更加適應容器化環境,在雲計算時代依舊緊跟主流,保持競爭力;
全系列文章分為主題和輔助兩部分,主題部分如下:
為了讓應用更適應容器化環境,SpringBoot2.3版本推出了新的探針技術,《掌握SpringBoot-2.3的容器探針》系列旨在與您一起學習和實踐這些新技術,分為三個階段:
如下圖紅框所示,2.3版本的容器探針特性早在預覽版(v2.3.0.M4)就已經發布:
如今v2.3.0.RELEASE已發布,可以放心的學習和使用該特性了,首先把基礎知識點列出來,確保準備工作OK;
下面是掌握探針技術所需的基礎知識,也是本文的主要內容:
接下來逐個學習,有了這些知識積累,我們才能更好的閱讀官方資料,開發適合自己業務場景的探針;
首先,SpringBoot為kubernetes提供了兩個actuator項,但是那些並未部署在kubernetes的SringBoot應用呢?用不上這兩項也要對外暴露這兩個服務地址嗎?
其次,就緒探針是什麼時候開始返回200返回碼的?應用啟動階段,業務服務可能需要一段時間才能正常工作,就緒探針要是提前返回了200,那k8s就認為容器可以正常工作了,這時候把外部請求調度過來是無法正常響應的,所以搞清楚就緒探針的狀態變化邏輯很重要;
最後,也是最重要的一點:有的場景下,例如外部依賴服務異常、本地全局異常等情況下,業務不想對外提供服務,等到問題解決后業務又可以對外提供服務了,如果此時我們能自己寫代碼控制就緒探針的返回碼,那就做到了控制kubernetes是否將外部請求調度到此容器上,這可是個很實用的功能!
面對上述三個問題您是否會感慨:看似簡單的容器探針技術,想要用好還需掌握更多知識,接下來的文章中咱們一起努力吧,從知識覆蓋到實戰操練,終究會掌握這門實用技術;
https://github.com/zq2599/blog_demos
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
動力總成兩者都是1。5L自吸發動機+5擋手動的動力組合。漢騰X7最大可輸出149匹馬力和215牛米,在2000轉才爆發最大扭矩到4000轉就開始衰減。而斯威X7最大可輸出156馬力和230牛米,在1750轉便爆發最大扭矩,可持續到4500轉。從賬面來看,斯威X7的動力要優於漢騰X7,出力也更早,同時還是直噴發動機,能更好地減少燃油損失。
前言
自主品牌有不少車型都喜歡用X7來命名,所以一說起X7總能提起一堆車型。最近上市了兩款X7,分別屬於漢騰和斯威的,這兩個牌子也是新近建立的汽車廠商,兩款車型的對碰到底會擦出什麼火花,讓我們拭目以待。
為了公平起見,我會拿漢騰X7的手動豪華型與斯威X7的手動舒適型來對比,兩者價差為1100元。
漢騰汽車-漢騰X7
2016款 1.5T 手動豪華型
指導價:8.98萬
華晨鑫源-斯威X7
2016款 1.5T 手動舒適型
指導價:9.09萬
外觀設計
漢騰X7的前臉看起來比較扁平,整個設計拉得比較寬,給人一種大車感。尾部則更為簡約,雙邊兩出的排氣口顯得較為大體。
斯威X7從前面看,會覺得有一點點像寶馬X3,特別是那兩組圓形大燈,看起來與寶馬的天使之瞳更為相像。大面積的鍍鉻格柵也很符合國人的口味。側面看,斯威X7像一台升高版的MpV。尾部設計則比較緊湊,最具亮點是那個M標識,像極了寶馬M系的標識,只是那幾條斜線的顏色有所不同。
內飾設計
漢騰X7的中控會稍向駕駛員一側傾斜,木紋裝飾板也顯得很高級,用料方面對得起這個價格。只是那塊中控屏幕有點汽配城的feel,拖累整個內飾觀感。
斯威X7的中控設計則看起來先進許多,與榮威RX5的類似,也是一整塊觸控屏,乾淨整潔,會比較耐看。
動力總成
兩者都是1.5L自吸發動機+5擋手動的動力組合。漢騰X7最大可輸出149匹馬力和215牛米,在2000轉才爆發最大扭矩到4000轉就開始衰減。而斯威X7最大可輸出156馬力和230牛米,在1750轉便爆發最大扭矩,可持續到4500轉。從賬面來看,斯威X7的動力要優於漢騰X7,出力也更早,同時還是直噴發動機,能更好地減少燃油損失。
底盤表現
漢騰X7的后懸架用的是多連桿獨立懸架,濾震動作會比較富有韌性,迎接衝擊時,有點點硬。不過整體還是偏軟,過彎時的側傾較大。
斯威X7的后懸架用的是扭力梁,不過調校得還是很不錯。在過一些減速帶時,對車子的小彈跳抑製得很到位。
空間表現
↑漢騰X7的後座↑
↑漢騰X7的後排中央↑
↑斯威X7的後排↑
兩者的空間都屬於中規中矩那種,但是漢騰X7的後排中央會有一點凸起,而斯威X7則幾乎全平。關鍵是,斯威X7是一款7座車型,第三排短途坐一下乘客是完全沒有問題的。不過斯威X7的第二排座椅不可前後調節,即便如此空間也很可觀。
配置對比
從配置的對比上來看,兩者各有千秋,不過漢騰X7上到豪華型也沒有車身穩定系統,這點很說不過去。具體的對比,可以看下圖。
編者總結
兩輛X7的對比上,斯威X7會更為佔優,具體表現在動力、空間表現與配置都要優於漢騰X7。作為兩個後起之秀,這樣的定價確實也是足夠親民。雖然許多方面都不佔優勢,但是漢騰X7較為傳統的內飾設計,也許可以俘獲不少大叔的芳心。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
但是就是銷量一般,很大一部分原因是消費者對自主品牌的不信任,也有一些人覺得艾瑞澤7價格稍貴,畢竟它的親弟弟艾瑞澤5的銷量非常好。艾瑞澤7的售價區間為7。99-9。59萬元,1。5T車型的起售價也是為7。99萬元,預測同等配置的1。
最近,我們得到了一些關於奇瑞艾瑞澤7的1.5T自動擋車型的路試照片,新車將會在2017年上市銷售。現款的艾瑞澤7的1.5T車型只配備了5擋手動變速箱,這對於那些既想要大動力又想要自動擋的消費者就感到比較痛苦了。
選擇1.6L車型吧,動力較弱。選擇1.5T車型吧!動力是足夠了,但是就是手動擋開着會比較累。所以奇瑞也意識到了這個問題,抓緊給艾瑞澤7的1.5T車型配備自動擋,豐富自己的產品線,以滿足消費者日趨多樣化的選擇。
從外觀上來看,這次曝光的1.5T自動擋車型和現款車型基本一致,畢竟只是增加了一款車型,又不是改款或者換代,所以外觀是不會有很大變化的。
前臉依舊是“X”造型的設計,前大燈靠內側部分採用了熏黑的處理,以增加運動感。現款車型已經是標配了日間行車燈了,作為自動檔車型自然不可缺少。尾部的“TCI”說明了它是1.5T車型的身份。
內飾也是和現款基本一致,看起來也比較時尚,目測做工應該不錯。主要的變化是由手動擋換車了自動擋。同時配置也比較豐富,ESp、上坡輔助、胎壓監測、倒車雷達、真皮方向盤、GpS、中控大屏幕、藍牙、手機映射等都會配備。
至於動力系統,新車肯定搭載1.5L渦輪增壓發動機,但是變速箱現在還沒有確定的消息,有可能是CVT,也有可能是和瑞虎7一樣的6速雙離合。不過,編者推測雙離合的概率會更大一點。
其實編者一直覺得艾瑞澤7是一款質感非常不錯的車子,四輪獨立懸挂,1.5T發動機動力十足,配置也很厚道,做工也不錯,行駛質感在同級別處於領先地位,操控較好。但是就是銷量一般,很大一部分原因是消費者對自主品牌的不信任,也有一些人覺得艾瑞澤7價格稍貴,畢竟它的親弟弟艾瑞澤5的銷量非常好。
艾瑞澤7的售價區間為7.99-9.59萬元,1.5T車型的起售價也是為7.99萬元,預測同等配置的1.5T自動擋車型將會比手動擋車型最低貴8000元,所以自動擋的售價將會接近9萬元。對於這個價格,你能接受么?
競爭對手
吉利帝豪GL
指導價:7.88-11.38萬
帝豪GL的1.3T自動擋車型的起售價為9.78萬,這價格要比艾瑞澤7稍微貴一點,但是帝豪GL的熱度要比艾瑞澤7大很多。作為目前比較熱門的一款車,帝豪GL將是艾瑞澤7的強大對手。
長安逸動
指導價:8.09-24.99萬
逸動的自動擋車型的起步價為8.99萬元,搭配4AT變速箱,目前還有着七八千的優惠,雖然價格可能比艾瑞澤7便宜,但是逸動的發動機為1.6L 125馬力。
一汽-大眾-寶來
指導價:10.78-15.38萬
寶來自動擋的起售價為11.98萬,目前也有着一兩萬的優惠,是加一些錢買寶來還是直接買艾瑞澤7?這是一個值得思考的問題。
所以說艾瑞澤7上市之後會面臨很多強大的對手,但是編者相信只要價格足夠接地氣,就不用發愁銷量問題。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計最專業,超強功能平台可客製化
※回頭車貨運收費標準
pytest+allure+jenkins進行接口測試、生成測試報告、結合jenkins進行集成。
pytest是python的一種單元測試框架,與python自帶的unittest測試框架類似,但是比unittest框架使用起來更簡潔,效率更高
allure-pytest是python的一個第三方庫。用於連接pytest和allure,使它們可以配合在一起使用。
allure-pytest基於pytest的原始執行結果生成適用於allure的json格式結果。該json格式結果可以用於後續適用allure生成html結果。
1、安裝pytest,命令行或終端中輸入
1 pip install pytest
2、安裝allure-pytest,安裝成功
1 pip install allure-pytest
allure-pytest安裝成功后截圖如下。
3、下載安裝JDK
官方下載:https://www.oracle.com/java/technologies/javase-jdk11-downloads.html
安裝與配置不作闡述請諒解
4、下載安裝Jenkins
官方下載:https://www.jenkins.io/
安裝與配置不作闡述請諒解
下載allure並配置
1、allure官網下載:https://github.com/allure-framework/allure2/releases
如下圖所示
2、allure2下載下來是一個zip的壓縮包,我們要解壓至自己的文件目錄下(可解壓放至項目的測試用例下或python安裝目錄下),自己可找到文件即可。
3、打開allure2目錄,找到bin目錄,複製bin文件目錄, 然後進行環境變量的配置,設置環境變量的目的就是讓系統無論在哪個目錄下都可以運行allure2。
4、環境變量設置:(桌面——我的電腦——右鍵屬性——高級系統配置——環境變量——系統變量——Path——編輯環境變量——把我們上面複製的目錄路徑新增至環境變量中即可)
設置環境變量,如下圖所示。
5、配置好后,打開cmd終端,輸入allure,出現以下幫助文檔,就說明配置成功了。
Allure裝飾器
上述我們講了一些理論的知識,下面我們就來實戰練習一下吧。進一步理解Pytest+allure如何結合應用的。
1、新建testcase文件夾,用來存放測試用例,新建test_Demo.py文件,作為pytest的具體測試用例文件。在test_Demo.py文件中輸入以下代碼。
1 # test_Demo.py 2 # Creator:wuwei 3 # Date:2020-06-09 4 5 import pytest 6 import requests 7 import allure 8 import sys 9 sys.dont_write_bytecode = True 10 11 @allure.epic('測試描述'.center(30, '*')) 12 @allure.feature('測試模塊') 13 @allure.suite('測試套件') 14 class TestPytestOne(): 15 @allure.story('用戶故事描述:用例一') 16 @allure.title('測試標題:用例一') 17 @allure.description('測試用例描述:用例一') 18 @allure.testcase('測試用例地址:https://www.baidu.com/') 19 @allure.tag('測試用例標籤:用例一') 20 def test_one(self): 21 print('執行第一個用例') 22 assert 1 == 1 23 24 @allure.story('用戶故事描述:用例二') 25 @allure.title('測試標題:用例二') 26 @allure.description('測試用例描述:用例二') 27 @allure.testcase('測試用例地址:https://www.sogou.com/') 28 @allure.tag('測試用例標籤:用例二') 29 def test_two(self,action): 30 print('執行第二個用例') 31 assert True == True 32 33 # pytest運行 34 if __name__ == "__main__": 35 pytest.main(['-s', '-v', 'test_Demo.py', '-q', '--alluredir', '../reports'])
2、我們再來創建一個conftest.py,conftest用來共享數據及不同層次之間共享使用的文件,測試用例的前置和後置中一般都可以用到的。
1 # conftest.py 2 # Creator:wuwei 3 # Date:2020-06-09 4 5 import pytest 6 import sys 7 sys.dont_write_bytecode = True 8 9 @pytest.fixture() 10 def action(): 11 print("測試用例開始".center(30, '*')) 12 yield 13 print("測試用例結束".center(30, '*'))
3、運行test_Demo.py文件,test_Demo文件中已經pytest+allure的結合,可查看allure的運行結果,可看出在根目錄中生成了一個reports文件夾,其中生成了測試報告的json文件,這裏面的json文件可通過allure生成html的測試報告。
運行test_Demo.py,終端显示如下圖所示。
生成的Json格式的測試報告,如下圖所示。
4、使用allure將json文件生成html的測試報告,定位至項目文件根目錄下,運行以下命令,會在項目根目錄下生成一個名為allure_reports的文件夾,用來存放html測試報告。命令下如所示。
1 allure generate reports -o allure_reports/
成功運行allure,結果如下圖所示。
項目根目錄下的allure_reports文件,存放的是allure生成的測試報告。可看出文件下有一個HTML文件,可通過Python的編輯器Pycharm來打開該HTML文件(測試報告),或可通過allure命令來打開該HTML,展示HTML測試報告。如下所示。
測試報告文件,HTML測試報告如下。
allure命令打開HTML測試報告。命令如下所示。
1 allure open allure_reports/
如下圖所示。
打開生成的HTML測試報告如下圖所示。
1、Jenkins插件網站上下載allure插件最新版本:
http://mirrors.jenkins-ci.org/plugins/allure-jenkins-plugin/
2、Jenkins的安裝我已經在Postman+Newman+Git+Jenkins的篇章中講過了,沒看小夥伴可以看一下那篇文章。確認Jenkins服務是否開啟。確認開啟后,在瀏覽器中輸入:http://localhost:8080/,進入Jenkins配置頁面。
3、http://localhost:8080/,登錄Jenkins的頁面,在管理Jenkins——插件管理——高級中找到上傳插件。將(1)步驟中下載的.hpi的文件上傳至jenkins上。
上傳安裝好的allure-jenkins-plugin的插件,安裝完成並成功,是藍色圓點显示,因我已經安裝過一次,會提示已經安裝,重啟Jenkins即可生效。(注意:不是關閉瀏覽器重新打開,而是重啟Jenkins服務)
4、全局變量中配置allure路徑與JDK的路徑,
配置JDK安裝的路徑,如下圖所示。
配置allure安裝的路徑,如下圖所示。
5、新建Item,配置構建后的allure測試報告生成。這裏配置Pytest執行完成之後,生成的allure文件所在的目錄位置。
項目中生成allure的json測試報告的位置。需與下面構建后操作中的Results的Path文件一致。
構建后操作的allure生成測試報告的配置,如下圖所示
6、配置構建命令。就是上述在cmd中運行項目時的命令。如下圖所示。
注意:運行后發現有報錯。“Build step ‘Execute Windows batch command’ marked build as failure”,解決方案,在運行項目的命令后添加exit 0。如下圖所示。
7、修改運行命令后我們再來運行一下。我們可發現運行后,allure裏面沒任務數據。因為我們還沒設置運行的項目路徑。設置工作空間,打開工作空間目錄,將我們的項目複製到jenkins的工作目錄中。
我們可將代碼傳至GitHub上,在Jenkins中設置相關Github項目的配置,也可進行Jenkins部署。我在Postman+Newman+Git+Jenkins這篇博客里就應用到了。有興趣的可參考看看這篇Jenkins如何Git項目。在這裏我們使用本地項目來部署。
測試報告無數據因為工作空間裏面沒有項目配置。
複製項目至Jenkins工作空間的目錄中。
8、添加項目后,我們再運行一下,藍點則為運行成功,可看到後面已經生成了allure的測試報告了。可直接點擊後面的alluree圖標跳轉至HTML的測試報告。如下圖所示。
allure生成的HTML測試報告
上述我們聊了下pytest+allure+jenkins如何結合集成一起使用的,本地啟動jenkins,運行項目,調用allure生成測試報告。也簡單的做了一個小Demo。後期我將結合Requests接口測試和seleniumWeb測試應用至具體項目中。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準