線程池運用不當的一次線上事故

在高併發、異步化等場景,線程池的運用可以說無處不在。線程池從本質上來講,即通過空間換取時間,因為線程的創建和銷毀都是要消耗資源和時間的,對於大量使用線程的場景,使用池化管理可以延遲線程的銷毀,大大提高單個線程的復用能力,進一步提升整體性能。

今天遇到了一個比較典型的線上問題,剛好和線程池有關,另外涉及到死鎖、jstack命令的使用、JDK不同線程池的適合場景等知識點,同時整個調查思路可以借鑒,特此記錄和分享一下。

01 業務背景描述

該線上問題發生在廣告系統的核心扣費服務,首先簡單交代下大致的業務流程,方便理解問題。

綠框部分即扣費服務在廣告召回扣費流程中所處的位置,簡單理解:當用戶點擊一個廣告后,會從C端發起一次實時扣費請求(CPC,按點擊扣費模式),扣費服務則承接了該動作的核心業務邏輯:包括執行反作弊策略、創建扣費記錄、click日誌埋點等。

02 問題現象和業務影響

12月2號晚上11點左右,我們收到了一個線上告警通知:扣費服務的線程池任務隊列大小遠遠超出了設定閾值,而且隊列大小隨着時間推移還在持續變大。詳細告警內容如下:

相應的,我們的廣告指標:點擊數、收入等也出現了非常明顯的下滑,幾乎同時發出了業務告警通知。其中,點擊數指標對應的曲線表現如下:

該線上故障發生在流量高峰期,持續了將近30分鐘后才恢復正常。

03 問題調查和事故解決過程

下面詳細說下整個事故的調查和分析過程。

第1步:收到線程池任務隊列的告警后,我們第一時間查看了扣費服務各個維度的實時數據:包括服務調用量、超時量、錯誤日誌、JVM監控,均未發現異常。

第2步:然後進一步排查了扣費服務依賴的存儲資源(mysql、redis、mq),外部服務,發現了事故期間存在大量的數據庫慢查詢。

上述慢查詢來自於事故期間一個剛上線的大數據抽取任務,從扣費服務的mysql數據庫中大批量併發抽取數據到hive表。因為扣費流程也涉及到寫mysql,猜測這個時候mysql的所有讀寫性能都受到了影響,果然進一步發現insert操作的耗時也遠遠大於正常時期。

第3步:我們猜測數據庫慢查詢影響了扣費流程的性能,從而造成了任務隊列的積壓,所以決定立馬暫定大數據抽取任務。但是很奇怪:停止抽取任務后,數據庫的insert性能恢復到正常水平了,但是阻塞隊列大小仍然還在持續增大,告警並未消失。

第4步:考慮廣告收入還在持續大幅度下跌,進一步分析代碼需要比較長的時間,所以決定立即重啟服務看看有沒有效果。為了保留事故現場,我們保留了一台服務器未做重啟,只是把這台機器從服務管理平台摘掉了,這樣它不會接收到新的扣費請求。

果然重啟服務的殺手鐧很管用,各項業務指標都恢復正常了,告警也沒有再出現。至此,整個線上故障得到解決,持續了大概30分鐘。

04 問題根本原因的分析過程

下面再詳細說下事故根本原因的分析過程。

第1步:第二天上班后,我們猜測那台保留了事故現場的服務器,隊列中積壓的任務應該都被線程池處理掉了,所以嘗試把這台服務器再次掛載上去驗證下我們的猜測,結果和預期完全相反,積壓的任務仍然都在,而且隨着新請求進來,系統告警立刻再次出現了,所以又馬上把這台服務器摘了下來。

第2步:線程池積壓的幾千個任務,經過1個晚上都沒被線程池處理掉,我們猜測應該存在死鎖情況。所以打算通過jstack命令dump線程快照做下詳細分析。

#找到扣費服務的進程號
$ ps aux|grep "adclick"

# 通過進程號dump線程快照,輸出到文件中
$ jstack pid > /tmp/stack.txth

在jstack的日誌文件中,立馬發現了:用於扣費的業務線程池的所有線程都處於waiting狀態,線程全部卡在了截圖中紅框部分對應的代碼行上,這行代碼調用了countDownLatch的await()方法,即等待計數器變為0后釋放共享鎖。

第3步:找到上述異常后,距離找到根本原因就很接近了,我們回到代碼中繼續調查,首先看了下業務代碼中使用了newFixedThreadPool線程池,核心線程數設置為25。針對newFixedThreadPool,JDK文檔的說明如下:

創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。如果在所有線程處於活躍狀態時提交新任務,則在有可用線程之前,新任務將在隊列中等待。

關於newFixedThreadPool,核心包括兩點:

1、最大線程數 = 核心線程數,當所有核心線程都在處理任務時,新進來的任務會提交到任務隊列中等待;

2、使用了無界隊列:提交給線程池的任務隊列是不限制大小的,如果任務被阻塞或者處理變慢,那麼顯然隊列會越來越大。

所以,進一步結論是:核心線程全部死鎖,新進的任務不對湧入無界隊列,導致任務隊列不斷增加。

第4步:到底是什麼原因導致的死鎖,我們再次回到jstack日誌文件中提示的那行代碼做進一步分析。下面是我簡化過後的示例代碼:

/*** 執行扣費任務 */
public Result<Integer> executeDeduct(ChargeInputDTO chargeInput) {  
    ChargeTask chargeTask = new ChargeTask(chargeInput);  
    bizThreadPool.execute(() -> chargeTaskBll.execute(chargeTask ));  
    return Result.success();
}

/*** 扣費任務的具體業務邏輯 */
public class ChargeTaskBll implements Runnable {  
    public void execute(ChargeTask chargeTask) {     
        // 第一步:參數校驗     
        verifyInputParam(chargeTask);     

        // 第二步:執行反作弊子任務     
        executeUserSpam(SpamHelper.userConfigs);     

        // 第三步:執行扣費     
        handlePay(chargeTask);     

        // 其他步驟:點擊埋點等     ...  
    }
}

/*** 執行反作弊子任務 */
public void executeUserSpam(List<SpamUserConfigDO> configs) {  
    if (CollectionUtils.isEmpty(configs)) {     
        return;  
    }  try {    
        CountDownLatch latch = new CountDownLatch(configs.size());    
        for (SpamUserConfigDO config : configs) {      
           UserSpamTask task = new UserSpamTask(config,latch);      
           bizThreadPool.execute(task);    
        }    
        latch.await();  
    } catch (Exception ex) {    
        logger.error("", ex);  
    }
}

通過上述代碼,大家能否發現死鎖是怎麼發生的呢?根本原因在於:一次扣費行為屬於父任務,同時它又包含了多次子任務:子任務用於并行執行反作弊策略,而父任務和子任務使用的是同一個業務線程池。當線程池中全部都是執行中的父任務時,並且所有父任務都存在子任務未執行完,這樣就會發生死鎖。下面通過1張圖再來直觀地看下死鎖的情況:

假設核心線程數是2,目前正在執行扣費父任務1和2。另外,反作弊子任務1執行完了,反作弊子任務2和4都積壓在任務隊列中等待被調度。因為反作弊子任務2和4沒執行完,所以扣費父任務1和2都不可能執行完成,這樣就發生了死鎖,核心線程永遠不可能釋放,從而造成任務隊列不斷增大,直到程序OOM crash。

死鎖原因清楚后,還有個疑問:上述代碼在線上運行很長時間了,為什麼現在才暴露出問題呢?另外跟數據庫慢查詢到底有沒有直接關聯呢?

暫時我們還沒有復現證實,但是可以推斷出:上述代碼一定存在死鎖的概率,尤其在高併發或者任務處理變慢的情況下,概率會大大增加。數據庫慢查詢應該就是導致此次事故出現的導火索。

05 解決方案

弄清楚根本原因后,最簡單的解決方案就是:增加一個新的業務線程池,用來隔離父子任務,現有的線程池只用來處理扣費任務,新的線程池用來處理反作弊任務。這樣就可以徹底避免死鎖的情況了。

06 問題總結

回顧事故的解決過程以及扣費的技術方案,存在以下幾點待繼續優化:

1、使用固定線程數的線程池存在OOM風險,在阿里巴巴Java開發手冊中也明確指出,而且用的詞是『不允許』使用Executors創建線程池。 而是通過ThreadPoolExecutor去創建,這樣讓寫的同學能更加明確線程池的運行規則和核心參數設置,規避資源耗盡的風險。

2、廣告的扣費場景是一個異步過程,通過線程池或者MQ來實現異步化處理都是可選的方案。另外,極個別的點擊請求丟失不扣費從業務上是允許的,但是大批量的請求丟棄不處理且沒有補償方案是不允許的。後續採用有界隊列后,拒絕策略可以考慮發送MQ做重試處理。— 結束 —

– End –

作者簡介:程序員,985碩士,前亞馬遜Java工程師,現58轉轉技術總監。持續分享技術和管理方向的文章。如果感興趣,可微信掃描下面的二維碼關注我的公眾號:『IT人的職場進階』

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

0基礎學算法 第七彈 位運算

  今天要講的內容主要分為兩個大塊,一個呢就是位運算,另一個呢就是關於二進制的原碼反碼補碼,位運算的應用也算是比較多的了,進行位運算有時候可以省略不少事情呢,當然,也不是說不會位運算就會天崩地裂,畢竟事實上,在學位運算之前,我都是用其他方法來強制性模擬位運算(當時我並不知道什麼是位運算)不過多學一些也是好的,省事兒,而且更快

  一,二進制的原碼反碼和補碼

  相信二進制沒有人不熟悉的,逢二進一,一個二進制數由0和1組成,他的最高位就是他的正負符號,0是正數,1是負數,例如10001000,這就是一個負的二進制數,當然最高位是0就是正數

  原碼之所以叫做原碼,必然是因為它就像是萬物之源,補碼、反碼都是在他的基礎上變化的,當最高位是0的時候,也就是正數的時候,原碼==反碼==補碼

  以下關於反碼及補碼的概念是基於最高位是1的情況(負數的情況)

  反碼,顧名思義,是原碼顛倒過來的樣子,除了最高位表示負號的那個“1”不變以外,其他的數位上的數  全部取反,1變0,0變1,假設原碼為1001010100,它的反碼就是1110101011,再假設原碼是0111001,它的反碼就是0111001,是不是有點奇怪,哈,我前面說過了,當原碼是正數的時候,原碼==反碼==補碼

  補碼,在原碼是正數的情況下是等於原碼或者反碼的,但是在負數情況下,他恰好是反碼加1,比如原碼是100000,反碼就是111111,補碼就是1000000

  二,進入正題,位運算

  在系統的講位運算之前,先給大家一張圖表,方便大家查詢

  

   註明:位運算符號在c++中用法同算數運算符

  首先是&

  其實位運算不複雜,理解了就好,比如說&,把他理解成1代表true,2代表false,1&&2,明顯為false(2),2&&2明顯也為false(2),只有1&&1的時候才為true(1);

  但是這是if語句裏面的判斷方法,而‘&’的運算方法是這樣的,比如1010&0011,運算過程如下圖

  過程如右圖

  至於|的話。。。就是上下兩個數只要有一個為1,結果就為1啦,如1100|0011=1111

  ‘~’!也比較好理解,做它的運算的時候,只需要傳一個二進制數,針對每一位進行運算可以從在每一位上如~1001=0110;

  ^,亦或,算起來也比較簡單,只要兩位不相等,結果就為1,舉例,1001^0101=1100

我覺得有圖就不用我多講了吧/狗頭/滑稽

   最後一組”<<“和”>>”這個是左移和右移的操作

  請看

  int類型的二進制是32位的,而long long是64位,左移和右移

  例如,給出一個標準的32的int類型00000000000000000000000000000001,當我們使用左移的時候,整體忘左移,並在後面補零

右移也相似,只不過是低位被擠掉了,高位補0

 

 

  有一道題,可以通過位運算做的很簡便,但如果你不會位運算的話。。。就另當別論了

  如果你不用位運算,你要寫很複雜的代碼,但是用了,你就只要10行代碼,如假包換(雖然我絕對不換)

  題目鏈接→https://www.luogu.com.cn/problem/P1100

   看到題目,很清晰了,首先錄入兩個字符串,然後將它轉成二進制,接着把前16位和后16位的數交換位置,最後結果轉成十進制輸出!好,完美。。。

  停!

 今天利用我們學的位運算壓根不用這麼麻煩!

  直接用上我們的左移”<<“和右移”>>”啊!

  只要左移后16位,右移前16位不就好了?

  廢話不多說,大家請看最短代碼!

#include<bits/stdc++.h>
using namespace std;
unsigned int x;
int main(){
    cin>>x;//錄入x
    cout<<((x<<16)|(x>>16));//將左移16位后的x和右移16位的x用或"|"將他們重新連起來
    return 0;//完美
}

  完美撒花

  如果覺得我講的不錯的,麻煩點個關注,點個贊,以後還會持續更新的

ps:歡迎大家進群 群號:1031457671

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

C#預處理器指令 -0016

預處理指令

這些指令/命令不會轉換為可執行代碼,但會影響編譯過程的各個方面;列如,可以讓編譯器不編譯某一部分代碼等。

C#中主要的預處理指令

#define和#undef

#define指令定義

#define DEBUG

它告訴編譯器存在DEBUG這個符號;這個符號不是實際代碼的一部分,而只是在編譯器編譯代碼時候可能會根據這個符號做條件編譯。

#undef定義:

#undef DEBUG

用來移除定義的符號DEBUG。如果不存在這樣的標記,#undef指令則不會生效。同樣,用#define再次定義一個同名的標記也不會有任何變化。

注意:

  1. 你需要將#define和#undef指令寫在實際業務代碼開始之前的位置。
  2. #define本身沒有什麼用,需要和其他預處理器指令結合使用;比如 #if

#if, #elif, #else和#endif

這些指令告訴編譯器是否要編譯包含在其中的代碼塊。例如:

int DoSomeWork(double x)
{
	// do something
	#if DEBUG
		Console.WriteLine($"x is {x}");
	#endif
}

這段代碼中的Console.Writeline語句,只有在前面用#define指令定義了符號DEBUG后才會在編譯的時候,真正被編譯到;

如果編譯器沒發現DEBUG符號,就會在編譯的時候忽略這句代碼。 

#elif(= else if)和#else指令可以用在#if塊中:

#define ENTERPRISE
#define W10
// further on in the file
#if ENTERPRISE
// do something
	#if W10
	// some code that is only relevant to enterprise
	// edition running on W10
	#endif
#elif PROFESSIONAL
// do something else
#else
// code for the leaner version
#endif

#if和#elif還支持有限的一些邏輯操作符,你可以用使用!,==,!=和||等。

一個標記如果存在,則認為是true,如果沒有定義,就認為是false,因此你也可以這樣使用:

#if W10 && (ENTERPRISE==false) // if W10 is defined but ENTERPRISE isn't

 

#warning和#error

 當編譯器遇到#warning的時候,會產生警告信息;

當編譯器遇到#error的時候,會產生錯誤信息;

    class Program
    {
        static void Main(string[] args)
        {

#warning this is a warning message which will be shown when compile

            Console.WriteLine("Hello World!");

#error this is a error message, and will break build
        }
    }

  編譯結果:

Program.cs(10,10): warning CS1030: #warning: 'this is a warning message which will be shown when compile' [/define_warning/define_warning.csproj]
Program.cs(14,8): error CS1029: #error: 'this is a error message, and will break build' [/define_warning/define_warning.csproj]
    1 Warning(s)
    1 Error(s)

  使用這些指令可以檢查#define語句是不是做錯了什麼事,使用#warning可以提醒要做些事情:

#if DEBUG && RELEASE
#error "You've defined DEBUG and RELEASE simultaneously!"
#endif
#warning "Don't forget to remove this line before the boss tests the code!"
Console.WriteLine("*I love this job.*");

  

#region和#endregion

 可以用來標識一段代碼,在Visual Studio或其他能夠識別的IDE里比較有用。

#region Member Field Declarations
int x;
double d;
Currency balance;
#endregion

  

#line

 #line指令可以用來改變編譯器輸出警告和錯誤時相應的文件名和行號信息。這個實際中,用的可能會比較少。

主要是在用第三方包的時候,有時候會導致編譯器報告的行號或文件名與實際不匹配。

#line可以用於還原這種匹配。

#line 164 "Core.cs" // We happen to know this is line 164 in the file Core.cs, 
// before the intermediate package mangles it.
// later on
#line default // restores default line numbering

  

#pragma

 #pragma指令可以用來終止或恢復某個指定編號到編譯器警告。

與命令行選項不同,#pragma指令可以在類或方法級別實現。

例如:

    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            Console.WriteLine("Hello World!");
        }
    }

  編譯是會有warning:

Program.cs(9,17): warning CS0219: The variable 'i' is assigned but its value is never used [/define_warning/define_warning.csproj]
    1 Warning(s)
    0 Error(s)  

 從warning信息可以看出是warning CS0219,加入#pragma后就不會有warning了。

#pragma warning disable CS0219
    public class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            Console.WriteLine("Hello World!");
        }
    }
#pragma warning restore CS0219

 注意:warning的代碼是區分大小寫的,CS2019要大寫,如果寫成cs2019則沒有用。 

 

學習資料

C#高級編程(第11版) C# 7 & .NET Core 2.0(.NET開發經典名著)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

Python流程控制語句詳解

1.程序結構

計算機在解決問題時,分別是順序執行所有語句、選擇執行部分語句、循環執行部分語句,分別是:順序結構、選擇結構、循環結構。如下圖:

2.選擇語句

  2.1最簡單的if語句

  Python使用保留字if來組成選擇語句,其格式如下:

if 表達式:
   代碼塊

  表達式可以是一個單純的布爾值或者變量,也可以是比較表達式或者邏輯表達式,如果表達式值為真,則執行“代碼塊”;如果值為假,就跳過“代碼塊”,執行後面語句,如圖

  

   注:1.在Python中,當表達式的值為非零的數或者非空的字符串時,if語句也認為是條件成立(即為真值)。

··     2.使用if語句時,如果只有一條語句,那麼語句塊可以直接寫到冒號“:”的右側。但為了代碼可讀性不建議這麼做。

     3.常見錯誤: 

1.if語句後面未加冒號
number = 3
if number == 3   #後面未加冒號,正確的是結尾處添加英文半角的冒號:if number == 3:
    print(number)
2.使用if語句時,如果在符合條件時,需要執行多個語句,一定要記得按照邏輯順序進行代碼縮進,否則程序的本意會有變化,但程序不會報錯,且bug不容易發現。
代碼1:
number = 3
if number == 3:
  print(number)
print('這個是5')
代碼2:
number = 3
if number == 3:
  print(number)
  print('這個是5'

  2.2.if  …… else 語句

  Python中提供 if …else 語句來解決兩個選擇問題,其格式如下:

if 表達式:
    語句塊1
else:
    語句塊2

  使用 if …else 語句時,表達式可以是一個單純的布爾值或者變量,也可以是比較表達式或者邏輯表達式,如果表達式值為真,則執行“代碼塊”;如果值為假,執行else後面的代碼塊。如圖所示:

  

  技巧:

if…else額語句可以使用條件表達式進行簡化,如下:
a = 5
if a > 0:
    b = a
else:
    b = -a
print(b)

簡化:
a = 5
b = a if a > 0 else -a
print(b)

  注:1. 在使用else 語句時,else一定不可以單獨使用,它必須和保留字if一起搭配使用。

    2.程序中使用if…else 語句時,如果出現多個if 語句多餘else語句的情況,那麼該else語句將會根據確定該else 語句屬於哪個if語句。

  2.3.if…elif…else語句

  在開發程序時遇到多選一的情況,則可以使用if …elif…else語句,具體情況如下:

if 表達式1:
    語句塊1
elif 表達式2:
    語句塊2
elif 表達式3:
    語句塊3
…
else:
    語句塊n

  使用 if …elif…else 語句時,表達式可以是一個單純的布爾值或者變量,也可以是比較表達式或者邏輯表達式,如果表達式值為真,則執行語句;如果值為假,則跳過該執行語句,進行下一個elif判斷,只有表達式全部為假的情況下,執行else後面的代碼塊。如圖所示:

   注:1. if 和 elif 都需要判斷表達式的真假,而 else 則不需要判斷;另外 elif 和 elif 都需要跟 if 一起使用,不能單獨使用。

     2. 使用if語句時盡量避免遵循以下原則:

(1).當使用布爾類型的變量作為判斷條件時,假設布爾類型變量為flag,較為規範格式;
if flag:   #表示為真
if not flag #表示為假
不符合規範格式:
if flag == True:
if flag == False:
(2).使用 " if 1 == a: " 這樣的書寫格式可以防止錯寫成 " if  a = 1: "這種形式,從而避免出錯 

  2.4 if 語句的嵌套

  前面已經介紹了3種形式的 if 語句,這三種都可以進行相互嵌套:

  (1) . 在最簡單的if語句中嵌套 if……else語句,形式如下:

if 表達式1: if 表達式2: 語句塊1 else: 語句塊2

  (2). 在if……else中嵌套if……else語句,形式如下:

if 表達式1:
    if 表達式2:
        語句塊1
    else:
        語句塊2
else:
    if 表達式3:
        語句塊3
    else:
        語句塊4

  注:if 選擇語句可以有多種嵌套方式,開發時可以可以根據自身的需要進行選擇合適的嵌套方式,但一定要嚴格控制好不同級別代碼的縮進量。

3.條件表達式

  在程序開發過程中,經常會根據表達式的結果,有條件的進行賦值,例如返回最大值:

a = 6
b = 3
if a > b:
    c = a
else:
    c = b

  針對以上代碼,使用條件表達式進行簡化,如下:

a = 6
b = 3
c = a if a > b else b

4.循環語句

  4.1 while 循環

  while循環是通過一個條件來控制是否要繼續反覆執行循環體(循環體是指一組被重複執行的語句)中的語句。

while 條件表達式:
    循環體

  當條件表達式的返回值為真時,則執行循環體中的語句,執行完畢后,重新判斷條件表達式的返回值,直到表達式返回的結果為假是退出循環體。

  

   注:在使用while循環語句時,一定不要忘記添加將循環條件改變為Flase的代碼,否則,將產生死循環。但開發中也離不開死循環,可根據情況進行編寫。

   4.2. for循環

  for 循環是一個依次重複執行的循環,通常適用於枚舉、遍歷序列和對象中的元素。語法如下:

for 迭代變量 in 可迭代對象:
    循環體

  迭代變量用於保存讀除的值,對象為遍歷或迭代的對象,該對象可以是任何有序的序列對象,如字符串,列表,元組等,循環體為一組被重複執行的語句。

  

  for循環語句可以最基本的應用就是進行數值循環和遍歷字符串。還可以進行遍歷列表、元組、集合和字典。

  4.3. 循環嵌套

  在Python中,是允許在一個循環體中嵌套另一個循環。

  (1). 在while循環中嵌套while循環

while 條件表達式1:
    while 條件表達式2:
        循環體2
    循環體1

  (2). 在for 循環中嵌套 for 循環

for 迭代變量1 in 對象1:
    for 迭代變量2 in 對象2:
        循環體2
    循環體1

  (3). 在while 循環中嵌套 for 循環

while 條件表達式:
    for 迭代變量 in 對象:
        循環體2
    循環體1

  (4). 在 for 循環中嵌套 while 循環

for 迭代變量 in 對象:
    while 條件表達式:
        循環體2
    循環體1

  特殊案例:九九乘法表

for i in range(0,10):
    for j in range(1,i+1):
        print(str(j) + "*"  + str(i) + "=" + str(i * j) +"\t" ,end = " "
    print("")

5.跳轉語句

  當循環滿足一定條件時,程序會一直執行下去,如果需要在中間離開循環,也就是for循環結束重複之前,或者while循環找到結束條件之前,即break語句和continue語句。

  5.1 break語句

  break可以終止當前循環,包括for循環和while循環在內的所有控制語句。

  在while中使用break語句:

 

while 條件表達式1:
    執行語句
    if 條件表達式2:
        break

  在for 中使用break語句

for 迭代變量 in 對象:
    if 條件表達式:
        break

  

           while語句使用break                                                             for語句中使用break

  5.2.continue語句

  continue語句的作用沒有break語句強大,他只能終止本次循環而提前進入下次循環中。

  在while中使用continue語句

while 條件表達式1:
    執行代碼
    if 條件表達式2:
        continue

  在for 中使用continue語句

for 迭代變量 in 對象:
    if 條件表達式:
        continue

         

            while語句使用continue                                                     for語句中使用continue

  注:break與continue的區別

    break語句一般會結合if 語句進行搭配使用,表示在某種條件下,跳出循環。如果使用嵌套循環,break語句將跳出最內層循環。

    continue語句一般也會結合if語句進行搭配使用,表示在某種條件下,跳出當前循環的剩下語句,繼續進行下一輪循環,如果使用嵌套循環,continue語句將只跳過最內層循環中剩餘語句。

6.pass語句

  在Python中pass語句表示空語句,它不做任何事情,一般起到站位作用,常用在代碼調試等。

#例:
for i in range(1,10):    #輸出1~10的數不包含10
    if i % 2 == 0:         #判斷是否是偶數   
        print(i,end="")     #在同一行打印偶數 
    else:                      #不是偶數
        pass                    #佔位符,不做任何事情,直接跳過

#輸出結果為:
2 4 6 8

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

這不是紀念品!法遊客欲帶走1.8公斤沙子 挨罰逾3萬

摘錄自2020年9月8日自由時報報導

法國1名遊客日前疑將4磅重(約1.8公斤)的當地海灘沙子裝在行李中的瓶子,試圖帶離薩丁尼亞島,被查獲後於卡利亞里埃爾馬斯機場被捕,據稱被罰1000歐元(約新台幣3萬4558元)。

《CNN》報導,薩丁尼亞島2017年起規定,從島上取沙屬非法行為,若帶走恐面臨罰款甚至入獄。當地森林保護員發言人表示,該遊客裝紗的瓶子已沒收,並指祭出罰則是因類似情形層出不窮,尤其是針對粉紅、白色等顏色特殊的沙子。據稱,有網站將島上沙子作為紀念品出售。

報導指出,該發言人提及,罰金落在500至3000歐元(約新台幣1萬7275至10萬3649元)區間,視取走來源與數量決定,過去3年,管制、裁罰變得更加嚴格。除了會與警方合作,也接受民眾檢舉。

海洋
環境新聞
國際新聞
法國
海灘
保護海洋

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

神奇夢幻的藍色光芒 點亮愛爾蘭海灘

摘錄自2020年9月8日大紀元報導

在愛爾蘭南部的一座海灘上出現一種奇特景象,攜帶著發光物質的海洋生物照亮了拍打過來的海浪,呈現一片夢幻的螢光藍色。

其實,芳泉鎮海灘以生物發光這一獨特的自然奇觀而聞名於世。生物發光是諸如細菌、藻類、水母以及浮游生物等海洋生物發光現象的一種說法。這種現象可以發生在任何深度的海水中,但當它發生在淺水區時,就會產生特別超現實的景象。較溫暖的天氣使水溫大面積上升,能充分給予海洋生物所需的能量,從而使其發光更加明亮。

根據愛爾蘭海洋研究與保護組織的說法,在愛爾蘭水域,生物發光是由稱為夜光藻(Noctiluca scintillans)的單細胞鞭毛藻發出的,這種藻類通常被稱為「星光海」(Sea Sparkle)。

海洋
環境新聞
國際新聞
愛爾蘭
海灘

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

餐桌上的減碳計畫:智庫研究改善糧食系統 可實現2050年減碳目標20%

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

還在看大眾?還不如買個豪華品牌SUV才20萬出頭

謳歌的設計風格一向都是未來感十足的CDX同樣不例外,“鑽石五邊形”的中網設計、修長的發動機蓋、外加整車凌厲的車身線條,整車給人一種力量肌肉的感覺。日系豪華品牌的內飾設計向來都令人滿意,雙色調的內飾風格、皮質裹的中控台以及車門,在車內你能接觸到的位置幾乎都用上了軟質材料包裹,材料足夠厚道。

下面就來推薦幾台預算25萬,既能日常在高端商務場合有范兒,周末偶爾激情迸發時飈一下也能輕鬆秒掉周圍一切豪華品牌SUV給大家,看看是否符合您的胃口。

●北京奔馳-奔馳GLA

新車指導價:26.98-39.80萬

其實在GLA上市之初,本着不大看好的態度去看這款車,跨界的造型、既沒有SUV的高大威猛也沒有小轎車高貴奢華。都說奔馳的設計總是走在時代的前端,這款車上市后不僅銷量節節升攀高甚至出現了加價購車的現象。到底是什麼原因促使這樣一款看似不起眼的cross車型車型得以熱賣呢?

究其原因就是奔馳對它的精確定位。在25-30萬這個價格區間,如果我們需要一台高貴的座駕我們大可選擇一款C級或是3系,而倘若我們需要的是一台拖家帶口的SUV這個價位也只能選擇合資品牌的緊湊級SUV。顯然這兩者消費人群都並非是這類型豪華品牌小型SUV的目標消費者。

而真正會選購它的人群應該是那些對生活品質有追求、不需要拖家帶口、希望買到一台個性豪華細膩的座駕,平時用於CBD地區通勤希望車子不要太大,而周末又想去周邊享受假日的寧靜需要一台擁有四驅系統通過性較好的車子。而奔馳的GLA不正是這類人群的所需要的嗎?

●一汽-大眾奧迪-奧迪Q3

新車指導價:23.42-34.49萬

同屬於BBA軍團的奧迪Q3這次則與奔馳背道而馳,外形回歸以往奧迪的家族化風格,遠遠望去的第一眼幾乎和老大哥Q5如出一轍。這樣的家族化設計最顯而易見的好處就是讓Q3看起來硬生生將自己拔高了好幾個級別,瞬間逼格滿滿。

向大哥Q5靠攏可不是什麼壞事,至少在內飾的做工用料上都是表現得跨級別的水平。中控台的布局滿滿的奧迪風味,整體設計布局非常簡潔流暢但不失精緻,體現出了針對年輕人群的時尚與動感。

對於那些追求科技喜歡前衛的朋友,推薦你們買奧迪Q3絕對令你們滿意。奧迪對科技和技術的推崇在Q3上體現得淋漓盡致,缸內直噴、濕式雙離合變速器、电子手剎、Autohold、奧迪坡路啟動輔助系統、全景天窗、自動防眩目內后視鏡、雙安全氣簾等等科技配置均為奧迪Q3全系標配。

●廣汽謳歌-謳歌CDX

新車指導價:22.98-31.28萬

相比起德系BBA的萬人追捧,日系豪華品牌在華的表現卻一直不盡如人意。就拿本文的主角CDX來說,謳歌的品牌入華也有將近十年了,但卻一直走高冷低調的路線而不被國人所認可,品牌認識度不高註定着銷量的慘淡。

但此次上市的CDX卻一反以往謳歌的作風,放下低調高冷的身份,與其同走非凡之路……謳歌的設計風格一向都是未來感十足的CDX同樣不例外,“鑽石五邊形”的中網設計、修長的發動機蓋、外加整車凌厲的車身線條,整車給人一種力量肌肉的感覺。

日系豪華品牌的內飾設計向來都令人滿意,雙色調的內飾風格、皮質裹的中控台以及車門,在車內你能接觸到的位置幾乎都用上了軟質材料包裹,材料足夠厚道。而當我們將目光轉移到擋把上,电子按鍵式的擋板設計配合上HUD抬頭显示簡直男女通吃。

在CDX車內YY了這麼久是時候帶它飛一下了。本田看家的1.5T發動機,在CDX上被調教到了182ps,從數據來看幾乎和同級別2.0T發動機平起平坐。與之匹配的是稱在思鉑睿上面飽受稱讚的8速雙離合變速箱,在這個以擋位數論英雄的時代CDX同樣出於領先地位。

但后懸挂依舊採用了半獨立懸挂的形式,這點着實讓人有點不爽,拋開物理形式就單純的駕駛感覺而言,其實表現得還算一絲不苟,日常駕駛絲毫不會覺得底盤有多餘的彈跳或是晃動,總之還是那句話,底盤好不好主要看調教。

●英菲尼迪(進口)-英菲尼迪QX30

新車指導價:24.98-38.98萬

“你上面推薦的都是國產貨,我還是覺得國外的月亮比較圓”你還別說進口車的情節在我們國內還是挺普遍的,買個豪華品牌不是進口車寧可不買的大有人在。那麼對於那些愛好進口車的網友這款英菲尼迪QX30絕對就是你的 “菜”了。

細心的網友不難發現,這QX30長得和奔馳的GLA好像啊。其實並非好像而是QX30的底子下就是一台GLA,在這個同質化嚴重的時代,網紅們幾乎都是一個樣,只要有一樣東西引領了潮流必然一堆爭相效仿。對於同質化這事其實並不在乎,至少在QX30上面我們見到了一個顏值更高的cross車型。

當你進入車內,倘若將方向盤上面的英菲尼迪標遮住,你絕對會犯渾這到底是台奔馳還是英菲尼迪?但當你下意識去撥動方向盤旁邊的懷擋時會讓你瞬間回歸,嗯這不是奔馳。內飾的配色以及風格都是東方人所喜愛的,上黑淺白的配色讓感覺賞心悅目,嗯~還是日韓風比較符合審美,歐美太重口味了。

駕駛感覺不用多言,你把它理解成一台披着日系車外殼的德系車就沒錯了。底盤紮實緊湊,支撐性以及厚重感都非常出色,cross車型的低重心優勢在QX30上同樣發揮得淋漓盡致,完全沒有同價位SUV那種晃晃悠悠的感覺,配合上指向性非常精準的轉向,駕駛感覺可以給滿分。

總結:綜上四台車各有各的特點,奔馳GLA精準的內飾以及德系濃濃的豪華車氛圍讓感覺是這四台車中最具有豪華氛圍的車型。奧迪Q3大量高科技配置的堆砌、外加神似大哥Q5的家族化設計讓它看起來更高端大氣。而CDX則保持了本田一向對豪華車的理解,既要內飾豪華又要前衛並且動力系統也是這幾台車裡最富有激情的。QX30簡單一句話說就是一款日系的奔馳,外觀內飾的設計更符合東方人的審美,而駕駛感受卻是地地道道的歐系風格,買一台車感受兩個品牌的精髓沒什麼不好的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

近期最火的7座家用車才8萬不到,6款車型究竟怎麼選?

它也是我們今天的主角——比亞迪宋MAX。而在空間方面,比亞迪宋MAX採用了2+3+2的座位布局,而寶駿730和歐尚A800均採用了2+2+3的座位布局,而在內部空間方面,歐尚A800與寶駿730的實際表現更佳,不過對於一款7座MpV來說,比亞迪宋MAX雖然在空間方面稍顯劣勢,但已經足夠使用,而且二排座椅也同樣支持前後移動,所以足夠我們日常7人的出行所需。

其實在不少消費者心目當中,MpV都是與麵包車劃上等號,而在合資品牌當中,奧德賽和GL8便是其中的最受歡迎車型,但是他們在售價方面,超過20萬的車價也並不是每位消費者都能消費得起!值得一說的是,我們今天的主角也同樣屬於爆款車型,而最讓我們驚喜的是,它的起售價才7.99萬起!

它也是我們今天的主角——比亞迪宋MAX!

而在空間方面,比亞迪宋MAX採用了2+3+2的座位布局,而寶駿730和歐尚A800均採用了2+2+3的座位布局,而在內部空間方面,歐尚A800與寶駿730的實際表現更佳,不過對於一款7座MpV來說,比亞迪宋MAX雖然在空間方面稍顯劣勢,但已經足夠使用,而且二排座椅也同樣支持前後移動,所以足夠我們日常7人的出行所需。

而針對於另外兩款競品車型,其實不難發現在設計方面兩款競品車型依然充滿着濃濃的麵包車氣息,而對於比亞迪宋MAX來說,無疑更適合居家使用,而它能擁有如此高的顏值還是得益於剛從奧迪挖角過來的設計總監艾格的功勞。而還是希望比亞迪能給我們帶來更多高顏值的產品!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

車價太高等等就能便宜?你可能想得有點多了!

另外,對於是買你上的車型來說,大多數都是3年一改款、5年一換代的節奏,所以正處於這兩個階段的在售車型在終端優惠也是最大的,而值得一說的是,這類車型無論再怎麼等,在最終的售價方面也不會出現太大差距,所以此時的建議是早買早享受。

你認為最有效的砍價套路是什麼?

在看來最佳的砍價技巧是以退為進!這一個砍價方式也被應用在我們的日常生活當中,譬如看上了某樣商品,在跟商家扯皮條的時候報出心理價位,然後假裝不買走人。據不完全統計,這種砍價方式的成功率高達80%。

雖然以上是我們的慣用手法,但值得一說的是,使用這一種方式購車的成功率會大大下降,畢竟買車並不是買一顆白菜這麼簡單!有不少消費者在購車時遇到的問題便是車價過高超過了原本的預算。

於是也出現了最普遍的做法:嫌差價太高,決定先回去考慮,等上一段時間,等優惠更大的時候再入手。

然而問題來了!

價格太高等一等

會不會有更大的優惠呢?

答案是:會!但是並非所有車型都通用,首先你必須明白,汽車在原則上來說都算是一件商品而他們同樣存在着一個生命周期,分別是:市場導入期、成長期、成熟期、衰退期。而根據這幾個階段,我們也可以總結出來:

除非是廠家在新款上市時主動放出來的優惠政策以外,正常的新款車型基本上都是沒有任何優惠的、甚至乎有部分車型還必須加價!譬如…

辣評:第十代思域的到來可以說是讓東本打了一仗漂亮的翻身仗,不僅如此,作為加價的鼻祖,旗下的車型CR-V依然賣得火熱,所以想等優惠?至少短期內並沒有。當然,如果你有充足的預算,原價購買也沒有任何問題。不過對於市面上大多數新車來說,上市后等上幾個月,這個時候車型的終端優惠也會相繼放出,此時購買的性價比會更高。

另外,對於是買你上的車型來說,大多數都是3年一改款、5年一換代的節奏,所以正處於這兩個階段的在售車型在終端優惠也是最大的,而值得一說的是,這類車型無論再怎麼等,在最終的售價方面也不會出現太大差距,所以此時的建議是早買早享受。

當然這是對於市面上90%的車型來說,有部分車型你想等它有優惠的活,還是建議你放棄,譬如…

辣評:雖然已經上市了很長一段時間,但受到代號為8AR-FTS的2.0T發動機產能的限制,漢蘭達目前依然處於一個供不應求的狀態,總結下來便是我明擺着要加價提車,不要拉倒!

購車時“拖”並不是一件好事!

車型優惠大意味着該款車型已經進入到了成熟期或者衰退期,而且價格越高的車型,優惠幅度也會越大!如果你選擇一等再等,你有可能會碰到這幾個問題:

1、老款換新款了!於是原有的優惠價格也會隨之上調,最後得不償失。

2、等上數月價格有所鬆動,但經不住國家的政策,譬如…

目前已經進入到了10月份,簡單來說還有2個月,我們即將迎來2018年!而值得一說的是,近幾年在購車政策上變動實在有點大,譬如像上一年購置稅還是5折,而2017年則改為7.5折,未來在2018年裡,購置稅是否恢復原價了?只能說有可能。

因小失大也是我們最常碰到的事情,所以在購車錢首先要搞清楚4S店的砍價方式:

購車時注意事項:

1、你能不能用心理價位買到心儀之車,決定權在4S店手上

2、銷售知識跟你談價格,而最終決定價格的是上一層的領導

3、砍價時需要具備必買之心,才能擁有資本與銷售談判

4、制定車型的心理價位,詳情可參考各大汽車門戶的車型落地價

砍價技巧:

1、不要相信銷售們的首次報價,坐下來慢慢詳談

2、在這家4S店砍完價再到另外一家4S店砍價(損招)

3、關係戶,起碼認識到經理級別以上的朋友,這樣價格才會低出新高度。

(找熟人購車需先注意):建議自己先砍好好價格,再找熟人看能不能獲取更大的優惠幅度,否則不僅欠了人情還買了高價車。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準