部落格

  • Gogoro升級iQ System 智慧系統3.1,搭載韌體升級FOTA新功能

    Gogoro升級iQ System 智慧系統3.1,搭載韌體升級FOTA新功能

    Gogoro Smartscooter 智慧雙輪的車主們,將能再度感受到,他們擁有的不僅是一台電動機車,而是一部不斷進化的雙輪電腦!Gogoro 於16 日正式推出搭載最新功能的「iQ System 智慧系統3.1」,包含空中韌體升級FOTA(Firmware-Over-The-Air)服務。這項科技可讓車主自行利用智慧型手機及Gogoro App 進行遠程升級,只要有穩定的網路訊號,車主都可以DIY 自行操作,無需再花時間返回服務中心。

    Gogoro 產品企劃經理王光祖表示:「Gogoro 擁有最先進、穩定、安全的技術, 也是目前台灣二輪車市場中,唯一運用空中韌體升級FOTA 的品牌。Gogoro 的車主不但可以透過韌體更新,感受智慧雙輪的聰明程度,更可隨著技術與功能的改進而持續升級,時時感受車子越騎越新的快感。」

    這是繼Gogoro 去年11月初發表「iQ System 智慧系統3.0」後,更進一步的升級版本。重點在於提供更直覺、方便、有感的使用體驗,包括更長時間的高速騎乘,更充沛的爬坡動力,以及從容地面對天氣變化,是第一台可以針對嚴寒或酷熱的氣候有動態調配溫度保護機制的二輪機車。其中在爬坡動力的改進,目前已經有車主在臉書分享經驗。游先生提到:「一路到山頂都是以前從來沒有過的充沛動力感!」網友Alex Yeh 則表示:「車速感覺不會掉太多,還是會有充沛的動力上山!」

    此外,因應部分Gogoro 車主曾發生不小心將手機或智慧鑰匙鎖在置物廂中的問題,之前在「iQ System 智慧系統3.0」時,便已在Gogoro App 中增加「車廂開關設定」的選項,置物箱打開後若在90 秒內關上時,就可以自動解鎖。 此次更新則更加優化了這個貼心的功能,即使在交換電池後,只要在放回電池的90 秒內關上座墊,一樣會自動解鎖,不需再次掏出智慧鑰匙即可上路,讓交換電池的過程更加流暢。

    (合作媒體:。圖片出處:Gogoro)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    ※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

    ※別再煩惱如何寫文案,掌握八大原則!

    ※網頁設計最專業,超強功能平台可客製化

  • 電動車商機大,Panasonic再收購Ficosa

    電動車商機大,Panasonic再收購Ficosa

     

    Panasonic 21日發布新聞稿宣布,將追加收購西班牙汽車零件大廠Ficosa International SA 20%股權,目標在今年4月把持股提高至69%、將Ficosa收編為旗下子公司行列,以期望藉由加深與Ficosa的合作,強化車用電子鏡、先進駕駛輔助系統(ADAS)等車用事業。Panasonic於2015年6月取得Ficosa 49%股權。

    Ficosa為全球第3大車用門鏡商,2015年營收為11億歐元,並計畫於2017年開賣與Panasonic攜手研發的新型車鏡。

    車用相關事業被Panasonic視為成長領域事業,Panasonic計畫在2018年度將車用相關事業營收提高至2兆日圓、將較2015年度的1.3兆日圓大增5成。

    路透社報導,Panasonic社長津賀一宏1月接受專訪時表示,「2兆日圓不是一個頂點,應該將目光瞄準在之後的2.5兆日圓或更高的目標上」。

    根據嘉實XQ全球贏家系統報價,截至台北時間22日13點18分為止,Panasonic大跌2.85%至1,209.5日圓,創約1個月來(2月10日以來)新低水準。

    日經新聞於去年12月5日報導,IT、電機大廠為了搶食自動駕駛、電動車(EV)普及所將引發的商機,正積極進行併購,其中日本Panasonic將收購歐洲車燈大廠ZKW、進軍車燈市場。據悉,Panasonic、ZKW已就收購進入最終協商階段,預估收購額最高將達1,000億日圓的規模。

    (本文內容由授權使用。圖片出處:Ficosa)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

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

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

    ※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

  • 大摩:特斯拉Model 3自駕系統車禍降低90%

    大摩:特斯拉Model 3自駕系統車禍降低90%

    電動車大廠特斯拉(Tesla Inc.)要價35,000美元的平價車種「Model 3」預計2017年稍晚就能開賣,摩根士丹利(通稱大摩)認為,這款電動車的安全度會是一般車輛的10倍之多,發生死亡車禍的機率有望比其他車種低90%。

    根據網友最近在YouTube貼出影片,自駕系統「Autopilot」能提前好幾步預測到車禍。()

    MarketWatch、Business Insider等外電報導,大摩分析師Adam Jonas 23日發表研究報告指出,特斯拉為每台車安裝超級電腦後,車子安全性提升至其他車輛兩倍已經不夠看,他相信Model 3的安全度會是其他車輛的十倍之多,這會讓死亡車禍的發生機會降低90%。

    Jonas認為,缺少特斯拉駕車輔助科技的二手車價值將因而猛掉,未來甚至會被禁止上路。特斯拉蒐集資料的能力超群,還能將先進的安全輔助技術應用到電動車,還未推出類似科技的傳統車廠,競爭力堪虞。

    假如Model 3大獲成功、數百萬輛擁有自駕功能的車輛上路,那麼對行車安全的統計資料應有影響,這會凸顯其科技的優異程度,並迫使主管機關下令要求所有車輛都必須配備類似的系統。

    特斯拉23日終場下跌0.09%、收254.78美元;年初迄今已大漲19.23%。

    不過,最近有人仔細挖掘特斯拉向美國證管會(SEC)呈交的最新10-K報告,發現該公司已在內文悄悄坦承,Model 3 Beta版的原型車,至今尚無蹤影。

    美國權威汽車雜誌《Car and Driver》記者Anton Wahlman 3月2日報導(),特斯拉在3月1日發布的10-K報告中表示,已在測試Model 3的設計和製程時開發出多種版本,可作為Model 3 Beta版原型車的候選選項;在董事會選定要用哪一款作為Beta版的原型車之後,特斯拉下一階段的工作里程碑就能順利達陣。

    Wahlman認為,這意味著特斯拉在該份公告涵蓋的期間內(截至2016年12月31日為止),仍舊還未完成Model 3 Beta版的原型車。不過,這份文件是在2017年3月1日發布,這或許也代表至3月初為止,董事會還未對要用哪款原型車拍板定案。

      (本文內容由授權使用。圖片出處:Public Domain)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

    ※別再煩惱如何寫文案,掌握八大原則!

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 騰訊入股Tesla,可望加速Model 3生產

    騰訊入股Tesla,可望加速Model 3生產

    騰訊於2017年3月29日宣布以17.8億美金買下Tesla 5%股權,此投資顯示騰訊對Elon Musk事業遠見之信心。騰訊資金的投入也有助於預定2017年底上市的Model 3。除此之外,Elon Musk在推特上回應關於太陽能瓦片的上市時間。

    外媒The Australian報導,汽車產業諮詢師Michael Dunne認為,騰訊旗下擁有中國最大的社群軟體微信(WeChat),如此有助於Tesla發展在中國生產製造的產線。Tesla過去曾和中國政府談論在中國建組裝廠一事,Elon Musk也表示若在中國建組裝廠不僅可以省下3分之1之成本,也可以省去進口關稅。

    根據中國的調查機構JL Warren Capital資料顯示,光是2016,中國向Tesla進口了11,839輛電動車,和2015相比成長了近5倍。騰訊與鴻海投資的新創汽車公司知行(FMC)預計在2020生廠自家的電動車,擁有Tesla股份也對FMC未來電動車布局有益。

    (首圖來源:Tesla)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    ※別再煩惱如何寫文案,掌握八大原則!

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

    ※超省錢租車方案

    ※FB行銷專家,教你從零開始的技巧

  • 中國新能源汽車補貼影響,比亞迪估Q1淨利將下滑

    中國新能源汽車補貼影響,比亞迪估Q1淨利將下滑

    比亞迪於28日晚間11點整公告表示,受新能源汽車補貼政策變動影響、產業短期會有所調整,2017年第1季集團新能源汽車業務也將承受一定壓力、預計新能源汽車銷量和盈利將有所下滑。此外,比亞迪也提到太陽能市場競爭依然激烈、產品價格壓力仍然較大。

    比亞迪表示,2016年底四部委發表的新能源汽車補貼新政將會對新能源汽車產業產生較大影響。由於2017年中國中央政府補貼額度比2016年降低20%、地方財政補貼不得超過中央財政單車補貼額的50%,一些規模較小的廠商及沒有實際競爭力的廠商可能會逐漸退出市場,整個市場的集中度將會進一步提升、龍頭企業將顯著受益,最終將促進新能源行業的持續健康發展。

    比亞迪預估2017年1-3月淨利將年減23.59-35.35%至5.5-6.5億元(人民幣)。

    比亞迪股份29日受上述消息衝擊而大幅走低。截至台北時間29日上午11時10分為止,比亞迪下跌2.81%至43.20港元;開盤迄今最低跌至42.85港元、創2月6日以來新低。

    股神巴菲特(Warren Buffett)掌管的波克夏海瑟威(Berkshire Hathaway)在2008年取得比亞迪10%股權。

    (本文內容由授權使用。圖片來源:比亞迪)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※別再煩惱如何寫文案,掌握八大原則!

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

    ※超省錢租車方案

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

    ※網頁設計最專業,超強功能平台可客製化

  • 擴電動車版圖,鴻海投資中國鋰電池廠

    擴電動車版圖,鴻海投資中國鋰電池廠

    鴻海自2014開始將旗下事業版圖擴大至電動車領域,3月29日公告於深圳的子公司富泰華工業以44.7億新台幣買下中國寧德時代新能源1.19%之股權,共擁有7,666,525股。

    母公司為鋰電池製造商新能源科技(ATL),寧德時代(CATL)主要研發生產電池芯、電池管理系統和動力電池系統,產品應用於電動車及儲能領域。

    鴻海董事長郭台銘事業瞄準電動車市場,繼2015年旗下富士康同騰訊及和諧汽車,三方合資成立和諧富騰以生產智慧電動車之後,和諧富騰於2016又投資成立初創公司Future Mobility Corporation(FMC),瞄準高階智慧電動車。

    FMC於2017年1月宣布將斥資116億元人民幣,於南京建高端智能電動車廠,第一期工程預計2019年可以完成。關於此次寧德時代的投資,董事長郭台銘表示為長期投資。

    (首圖來源:寧德時代)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

    ※超省錢租車方案

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 積極拓電動車市場,Hyundai推EV車用平台

    積極拓電動車市場,Hyundai推EV車用平台

     

    特斯拉(Tesla)電動車成功引領話題,各大車廠跟紛紛仿效,搶攻此一市場。原本力推燃料電池車的韓國車廠–現代汽車(Hyundai Motor),眼看市場風向轉變,也宣布要研發該公司首款電動車專屬的車用平台(car platform)。

    路透社30日報導,車用平台意指外觀不同的汽車共享相通的設計、工程、生產流程,以及主要零件等,能夠壓低研發成本。現代汽車之前力推燃料電池車,如今轉攻電池車,突顯投資人施壓,要求該公司積極進軍新市場。

    現代汽車的電動車平台,電池將位於汽車底部,以便容納大容量電池,並讓車內空間最大化。現代-起亞環保車主管Lee Ki-sang說,電動車平台初期投資費用高,但是他們必須替未來做準備。分析師說現代別無選擇,必須追隨特斯拉、通用汽車、Daimler AG旗下的賓士(Mercedes-Benz),打造單獨的電動車平台,才能留在此一市場。

    Hi Investment & Securities分析師Ko Tae-bong表示,單獨平台初始時可能會造成虧損,但是現代若不研發長程電動車將落後對手,例如300、500、600公里車款。

    電動車大廠特斯拉(Tesla Inc.)要價35,000美元的平價車種「Model 3」預計2017年稍晚就能開賣,摩根士丹利(通稱大摩)認為,這款電動車的安全度會是一般車輛的10倍之多,發生死亡車禍的機率有望比其他車種低90%。

    MarketWatch、Business Insider等外電報導,大摩分析師Adam Jonas 23日發表研究報告指出,特斯拉為每台車安裝超級電腦後,車子安全性提升至其他車輛兩倍已經不夠看,他相信Model 3的安全度會是其他車輛的十倍之多,這會讓死亡車禍的發生機會降低90%。

    Jonas認為,缺少特斯拉駕車輔助科技的二手車價值將因而猛掉,未來甚至會被禁止上路。特斯拉蒐集資料的能力超群,還能將先進的安全輔助技術應用到電動車,還未推出類似科技的傳統車廠,競爭力堪虞。

    (本文內容由授權使用。圖片出處:Hyundai)  

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

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

    ※FB行銷專家,教你從零開始的技巧

  • 博弈論——兩人取子遊戲與威佐夫博弈,隱藏在背後的黃金分割

    博弈論——兩人取子遊戲與威佐夫博弈,隱藏在背後的黃金分割

    本文始發於個人公眾號:TechFlow,原創不易,求個關注

    今天是算法和數據結構專題第25篇文章,我們繼續博弈論專題。

    在上一篇文章當中我們了解了最簡單的巴什博奕,今天我們來看看另一個經典的博弈模型——威佐夫博弈。博弈論和機器學習有些類似,數學家們針對場景進行建模,設計出了幾個經典模型。然後我們在面臨具體問題的時候,對問題進行深入分析,尋找最合適的模型應用來解決它。

    石子問題

    我們來看一道經典的例題,有兩堆石子,有兩個絕頂聰明的人在玩一個遊戲。每次每個人可以從其中一堆石子當中取走任意數量的石子,或者是從兩堆當中同時取走相同數量的石子。無法取石子的人落敗,請問,在告知兩堆石子數量的情況下,這兩個人當中哪一方會獲勝?

    我們簡單分析一下,會發現一些局面是先手必敗的。比如說(0, 0),再比如(1, 2)。我們簡單分析一下(1, 2),先手有4種策略,首先他可以取走第一堆,那麼後手可以取完第二堆,顯然後手獲勝。他也可以在第二堆當中取1個,這時剩下(1, 1),後手會同時取完,同樣是後手獲勝。第三種是他取走第二堆,後手可以取完第一堆,後手獲勝。第四種是他在第一堆和第二堆當中同時取走一個,這時第二堆剩下一個,後手勝。

    那麼,這些必敗的狀態之間有什麼規律呢?我們怎麼找到這個規律,並且找到解呢?

    分析

    我們可以枚舉幾個必敗的狀態:(0, 0), (1, 2), (3, 5), (4, 7)…

    我們觀察一下這些狀態,可以找到兩條規律。我們假設從小到大排的第k個必敗狀態是(x, y),並且x < y。我們可以發現y = x + k。也就是說必敗狀態兩個數的差值是遞增的,這也說明了每一個必敗狀態的差值都各不相同。

    其實這是很容易證明的,我們用反證法,假設(a, a+k), (b, b+k)都是必敗狀態,並且a < b。那麼先手在面臨(b, b+k)的時候,只需要在兩堆當中同時取走b-a個石子,那麼給後手的局面就是(a, a+k)。對於後手來說,這是一個必敗的局面,這就和(b, b+k)先手必敗矛盾,所以不存在兩個必敗局面的差值相等。

    我們也可以作圖分析,我們把兩堆石子的數量看成是坐標軸上的一個點。所以遊戲就變成了:棋盤上有一個點,每次每個人可以將它向下、向左或者向左下移動若干個格子,不能移動的人輸。終止節點顯然是原點,一步就能移動到原點的點顯然是必勝點,假設我們給這些所有必勝點都染色的話,剩下的的沒當中橫縱坐標和最小的點就是下一個必敗點。因為它不論如何移動,都會給對手留下一個必勝點。

    我們根據上面的邏輯把必敗點都染色,可以得到下面這張圖:

    從這張圖可以看出,必敗點之間不能通過一次移動得到,換句話說可以一次移動到必敗點的點都是必勝點,從圖上可以看出,除了必敗點的之外的點都是必勝點,並且每一個自然數都必然只會被包含在一個必敗狀態當中。

    到這裏,我們距離解法已經很接近了,現在剩下的問題是,我們如何根據x和y的取值快速判斷它們是否構成一個必敗局面呢?也就是說我們能不能找出一個通項公式,對於第k個必敗局面,它的坐標是(\(x_k, y_k\))呢?

    求解

    為了寫出通項公式,我們需要引入Betty定理。

    設a和b是兩個正無理數,並且\(\frac{1}{a} + \frac{1}{b} = 1\)

    記P={[\(a_n\)], \(n \in N^+\)}, Q={[\(b_n\)], \(n \in N^+\)},則\(P \cap Q = \varnothing\),\(P\cup Q = N^+\)

    證明

    \(P\cap Q = \varnothing\)

    反證,我們假設存在\(k \in P\)並且\(k \in Q\),即存在正整數n, m滿足 k < an, bm < k+1。

    也就是:\(\frac{n}{k} > \frac{1}{a} > \frac{n}{k+1}, \frac{m}{k} > \frac{1}{b} > \frac{m}{k+1}\)兩個式子相加可以得到:\(\frac{m+n}{k} > 1 > \frac{m+n}{k}\)

    即\(k < n+m < k+ 1\),這與n,m,k都是正整數矛盾

    \(P \cup Q = N^+\)

    反證,假設存在\(k \notin P\)且\(k \notin Q\),即存在正整數n,m滿足\(an < k < a(n+1)-1, bm < k < b(m+1)-1\)

    即:\(\frac{n}{k} < \frac{1}{a} < \frac{n+1}{k+1}, \frac{m}{k} < \frac{1}{b} < \frac{m+1}{k+1}\)

    相加,可以得到:\(\frac{m+n}{k} < 1 < \frac{n+m+2}{k+1}\)

    即:n + m < k < n + m + 1,這與n,m,k均為正整數矛盾

    我們花了這麼大力氣來證明Betty定理就是為了用的,因為我們發現必敗狀態的通項和Betty定理序列很像。我們不妨假設存在這樣的a, b同時滿足Betty定理與必敗狀態的性質:

    \[[an] + n = [bn], \frac{1}{a} + \frac{1}{b} = 1 \]

    \[[an] + n = [an + n] = [(a+1)n] = [bn] \]

    代入可以得到:

    \[\frac{1}{a} + \frac{1}{a+1} = 1 \]

    解這個方程,可以得到\(a = \frac{1 + \sqrt{5}}{2}\approx 1.618\),熟悉數學的同學相信一下就看出來了,這個數是黃金分割的比例,這是巧合嗎,還是藏着更深的道理呢?

    至少,求出了a之後,我們就可以非常簡單地判斷必敗狀態了:

    import math
    def lose_or_win(a, b):
        if a > b:
            a, b = b, a
        
        k = b - a
        # 根據差值k求出第k個必敗狀態,判斷是否相等
        return not (int(k * (math.sqrt(5)+1) / 2)) == a
    

    總結

    和之前介紹的巴什博奕相比,威佐夫博弈的推導過程要複雜得多,但是雖然推導過程依然複雜,但是仍然擋不住最後實現的代碼非常簡單。

    另外,在推導的過程當中,我們用到了Betty定理,這個定理的推導和證明雖然不難,但是如果不是數學專業的同學,可能大概率都沒有接觸過。這其實體現了博弈論本身和數學的關係是非常緊密的。一個看起來非常簡單的問題,引申出了一系列眼花繚亂的推導和證明,怎麼樣,大家看得還過癮嗎?

    今天的文章到這裏就結束了,如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

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

    【其他文章推薦】

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

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

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

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

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

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

  • Task.Result跟 Task.GetAwaiter.GetResult()相同嗎?怎麼選?

    前幾天在用線程池執行一些任務時運到一種情形,就是回調方法中使用到了異步方法,但是回調方法貌似不支持async await的寫法。這時候我應該如何處理呢?是使用Task.Result來獲取返回結果,還是使用GetAwaiter.GetResult()呢?本文就來探討下吧。

    作者:依樂祝

    原文地址:https://www.cnblogs.com/yilezhu/p/13168337.html

    這裏先上我這種場景的偽代碼:

    ThreadPool.QueueUserWorkItem(ExcuteScanProcess, node);
    

    在ExcuteScanProcess這個回調方法中

    private void ExcuteScanProcess(object state)
    {
        ……其他處理……
        repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();
        ……其他處理……
    }
    

    如上圖所示repository.UpdateAsync(node)屬於一部方法,這時候我想要等待它異步執行完成之後再執行後續的邏輯。這時候我有兩種選擇,是直接

    repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();
    

    好呢,還是

    repository.UpdateAsync(node).ConfigureAwait(false).Result;
    

    好呢?

    為此我查找了相關的資料,對它倆的區別做一個簡單的總結:

    其實這兩個使用方式是差不多的。不過,還是有一點小小的區別的:如果任務失敗,Task.GetAwaiter().GetResult()會直接拋出異常,而Task.Result則會把異常包裝在AggregateException中。從這個角度說Task.GetAwaiter().GetResult()要優於Task.Result。畢竟它少了異常的包裝操作,即直接拋出異常,而不是把異常包裝在AggregateException中。

    下面的引言解釋了為什麼Task.Result不僅僅包含Task.GetAwaiter().GetResult()(由於“非常高的兼容性”)的異常傳播行為。

    如前所述,我們有一個非常高的兼容性標準,因此我們避免了改動。因此,Task.Wait保留了始終包裝的原始行為。但是,您可能會發現自己處在某些高級情況下,這些情況下您想要的行為類似於所採用的同步阻塞Task.Wait,但是您希望將原始異常展開而不是傳播,而不是將其封裝在AggregateException中。為此,您可以直接定位任務的等待者。當您編寫“ await task;”時,編譯器Task.GetAwaiter()會將其轉換為方法的用法,這將返回具有GetResult()方法的實例。當用於有故障的任務時,GetResult()將傳播原始異常(這是“ await task;” 如何獲得其行為)。因此,您可以使用“task.GetAwaiter().GetResult()如果您想直接調用此傳播邏輯。

    https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

    “ GetResult”實際上表示“檢查任務是否有錯誤”

    通常,我會儘力避免對異步任務進行同步阻塞。但是,在少數情況下,我確實違反了該準則。在那些罕見的情況下,我的首選方法是GetAwaiter().GetResult()因為它保留任務異常,而不是將它們包裝在中AggregateException。

    總結

    通過上述內容的闡述,因此在那些必須對異步任務進行同步阻塞的場景中,我選擇使用GetAwaiter().GetResult()。

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

    【其他文章推薦】

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

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

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

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

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

  • 這一次搞懂Spring Web零xml配置原理以及父子容器關係

    這一次搞懂Spring Web零xml配置原理以及父子容器關係

    前言

    在使用Spring和SpringMVC的老版本進行開發時,我們需要配置很多的xml文件,非常的繁瑣,總是讓用戶自行選擇配置也是非常不好的。基於約定大於配置的規定,Spring提供了很多註解幫助我們簡化了大量的xml配置;但是在使用SpringMVC時,我們還會使用到WEB-INF/web.xml,但實際上我們是完全可以使用Java類來取代xml配置的,這也是後來SpringBoott的實現原理。本篇就來看看Spring是如何實現完全的零XML配置。

    正文

    先來看一下原始的web.xml配置:

    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
          <!--加載spring配置-->
          classpath:spring.xml
        </param-value>
      </context-param>
      <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>ServicePlatform.root</param-value>
      </context-param>
    
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        <!--<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>-->
      </listener>
    
      <servlet>
        <servlet-name>spring-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <!--springmvc的配置文件-->
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring-dispatcher.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>spring-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>
    </web-app>
    

    這裏各個配置的作用簡單說下,context-param是加載我們主的sping.xml配置,比如一些bean的配置和開啟註解掃描等;listener是配置監聽器,Tomcat啟動會觸發監聽器調用;servlet則是配置我們自定義的Servlet實現,比如DispatcherServlet。還有其它很多配置就不一一說明了,在這裏主要看到記住context-param和servlet配置,這是SpringIOC父子容器的體現。在之前的I文章中講過IOC容器是以父子關係組織的,但估計大部分人都不能理解,除了看到複雜的繼承體系,並沒有看到父容器作用的體現,稍後來分析。
    了解了配置,我們就需要思考如何替換掉這些繁瑣的配置。實際上Tomcat提供了一個規範,有一個ServletContainerInitializer接口:

    public interface ServletContainerInitializer {
        void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
    }
    

    Tomcat啟動時會調用該接口實現類的onStartup方法,這個方法有兩個參數,第二個不用說,主要是第一個參數什麼?從哪裡來?另外我們自定義的實現類又怎麼讓Tomcat調用呢?
    首先解答最後一個問題,這裏也是利用SPI來實現的,因此我們實現了該接口后,還需要在META-INF.services下配置。其次,這裏傳入的第一個參數也是我們自定義的擴展接口的實現類,我們可以通過我們自定義的接口實現很多需要在啟動時做的事,比如加載Servlet,但是Tomcat又是怎麼知道我們自定義的接口是哪個呢?這就需要用到@HandlesTypes註解,該註解就是標註在ServletContainerInitializer的實現類上,其值就是我們擴展的接口,這樣Tomcat就知道需要傳入哪個接口實現類到這個onStartup方法了。來看一個簡單的實現:

    @HandlesTypes(LoadServlet.class)
    public class MyServletContainerInitializer implements ServletContainerInitializer {
        @Override
        public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
            Iterator var4;
            if (set != null) {
                var4 = set.iterator();
                while (var4.hasNext()) {
                    Class<?> clazz = (Class<?>) var4.next();
                    if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()) && LoadServlet.class.isAssignableFrom(clazz)) {
                        try {
                            ((LoadServlet) clazz.newInstance()).loadOnstarp(servletContext);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    public interface LoadServlet {
    
        void loadOnstarp(ServletContext servletContext);
    }
    
    public class LoadServletImpl implements LoadServlet {
        @Override
        public void loadOnstarp(ServletContext servletContext) {
            ServletRegistration.Dynamic initServlet = servletContext.addServlet("initServlet", "org.springframework.web.servlet.DispatcherServlet");
            initServlet.setLoadOnStartup(1);
            initServlet.addMapping("/init");
    	}
    }
    

    這就是Tomcat給我們提供的規範,通過這個規範我們就能實現Spring的零xml配置啟動,直接來看Spring是如何做的。
    根據上面所說我們可以在spring-web工程下找到META-INF/services/javax.servlet.ServletContainerInitializer配置:

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    	@Override
    	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
    			throws ServletException {
    
    		List<WebApplicationInitializer> initializers = new LinkedList<>();
    
    		if (webAppInitializerClasses != null) {
    			for (Class<?> waiClass : webAppInitializerClasses) {
    				// Be defensive: Some servlet containers provide us with invalid classes,
    				// no matter what @HandlesTypes says...
    				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
    						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
    					try {
    						initializers.add((WebApplicationInitializer)
    								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
    					}
    					catch (Throwable ex) {
    						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
    					}
    				}
    			}
    		}
    
    		if (initializers.isEmpty()) {
    			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
    			return;
    		}
    
    		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
    		AnnotationAwareOrderComparator.sort(initializers);
    		for (WebApplicationInitializer initializer : initializers) {
    			initializer.onStartup(servletContext);
    		}
    	}
    
    }
    
    

    核心的實現就是WebApplicationInitializer,先看看其繼承體系

    AbstractReactiveWebInitializer不用管,主要看另外一邊,但是都是抽象類,也就是說真的實例也是由我們自己實現,但需要我們實現什麼呢?我們一般直接繼承AbstractAnnotationConfigDispatcherServletInitializer類,有四個抽象方法需要我們實現:

        //父容器
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class<?>[]{SpringContainer.class};
        }
    
        //SpringMVC配置子容器
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class<?>[]{MvcContainer.class};
        }
    
        //獲取DispatcherServlet的映射信息
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    
    	// filter配置
        @Override
        protected Filter[] getServletFilters() {
            MyFilter myFilter = new MyFilter();
            CorsFilter corsFilter = new CorsFilter();
            return new Filter[]{myFilter,corsFilter};
        }
    

    這裏主要注意getRootConfigClasses和getServletConfigClasses方法,分別加載父、子容器:

    @ComponentScan(value = "com.dark",excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
    })
    public class SpringContainer {
    }
    
    @ComponentScan(value = "com.dark",includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
    },useDefaultFilters = false)
    public class MvcContainer {
    }
    

    看到這兩個類上的註解應該不陌生了吧,父容器掃描裝載了所有不帶@Controller註解的類,子容器則相反,但需要對象時首先從當前容器中找,如果沒有則從父容器中獲取,為什麼要這麼設計呢?直接放到一個容器中不行么?先思考下, 稍後解答。
    回到onStartup方法中,直接回調用到AbstractDispatcherServletInitializer類:

    	public void onStartup(ServletContext servletContext) throws ServletException {
    		super.onStartup(servletContext);
    		//註冊DispatcherServlet
    		registerDispatcherServlet(servletContext);
    	}
    

    先是調用父類:

    	public void onStartup(ServletContext servletContext) throws ServletException {
    		registerContextLoaderListener(servletContext);
    	}
    
    	protected void registerContextLoaderListener(ServletContext servletContext) {
    
    		//創建spring上下文,註冊了SpringContainer
    		WebApplicationContext rootAppContext = createRootApplicationContext();
    		if (rootAppContext != null) {
    			//創建監聽器
    			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
    			listener.setContextInitializers(getRootApplicationContextInitializers());
    			servletContext.addListener(listener);
    		}
    	}
    

    然後調用createRootApplicationContext創建父容器:

    	protected WebApplicationContext createRootApplicationContext() {
    		Class<?>[] configClasses = getRootConfigClasses();
    		if (!ObjectUtils.isEmpty(configClasses)) {
    			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    			context.register(configClasses);
    			return context;
    		}
    		else {
    			return null;
    		}
    	}
    

    可以看到就是創建了一個AnnotationConfigWebApplicationContext對象,並將我們的配置類SpringContainer註冊了進去。接着創建Tomcat啟動加載監聽器ContextLoaderListener,該監聽器有一個contextInitialized方法,會在Tomcat啟動時調用。

    	public void contextInitialized(ServletContextEvent event) {
    		initWebApplicationContext(event.getServletContext());
    	}
    
    	 */
    	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    		long startTime = System.currentTimeMillis();
    		try {
    			// Store context in local instance variable, to guarantee that
    			// it is available on ServletContext shutdown.
    			if (this.context == null) {
    				this.context = createWebApplicationContext(servletContext);
    			}
    			if (this.context instanceof ConfigurableWebApplicationContext) {
    				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
    				if (!cwac.isActive()) {
    					// The context has not yet been refreshed -> provide services such as
    					// setting the parent context, setting the application context id, etc
    					if (cwac.getParent() == null) {
    						// The context instance was injected without an explicit parent ->
    						// determine parent for root web application context, if any.
    						ApplicationContext parent = loadParentContext(servletContext);
    						cwac.setParent(parent);
    					}
    					configureAndRefreshWebApplicationContext(cwac, servletContext);
    				}
    			}
    			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    
    			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    			if (ccl == ContextLoader.class.getClassLoader()) {
    				currentContext = this.context;
    			}
    			else if (ccl != null) {
    				currentContextPerThread.put(ccl, this.context);
    			}
    
    			return this.context;
    		}
    	}
    

    可以看到就是去初始化容器,這個和之前分析xml解析是一樣的,主要注意這裏封裝了ServletContext對象,並將父容器設置到了該對象中。
    父容器創建完成后自然就是子容器的創建,來到registerDispatcherServlet方法:

    	protected void registerDispatcherServlet(ServletContext servletContext) {
    		String servletName = getServletName();
    		Assert.hasLength(servletName, "getServletName() must not return null or empty");
    
    		//創建springmvc的上下文,註冊了MvcContainer類
    		WebApplicationContext servletAppContext = createServletApplicationContext();
    		Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
    
    		//創建DispatcherServlet
    		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
    		Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
    		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
    
    		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
    		if (registration == null) {
    			throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
    					"Check if there is another servlet registered under the same name.");
    		}
    
    		/*
    		* 如果該元素的值為負數或者沒有設置,則容器會當Servlet被請求時再加載。
    			如果值為正整數或者0時,表示容器在應用啟動時就加載並初始化這個servlet,
    			值越小,servlet的優先級越高,就越先被加載
    		* */
    		registration.setLoadOnStartup(1);
    		registration.addMapping(getServletMappings());
    		registration.setAsyncSupported(isAsyncSupported());
    
    		Filter[] filters = getServletFilters();
    		if (!ObjectUtils.isEmpty(filters)) {
    			for (Filter filter : filters) {
    				registerServletFilter(servletContext, filter);
    			}
    		}
    
    		customizeRegistration(registration);
    	}
    
    	protected WebApplicationContext createServletApplicationContext() {
    		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    		Class<?>[] configClasses = getServletConfigClasses();
    		if (!ObjectUtils.isEmpty(configClasses)) {
    			context.register(configClasses);
    		}
    		return context;
    	}
    
    

    這裏也是創建了一個AnnotationConfigWebApplicationContext對象,不同的只是這裏註冊的配置類就是我們的Servlet配置了。然後創建了DispatcherServlet對象,並將上下文對象設置了進去。看到這你可能會疑惑,既然父子容器創建的都是相同類的對象,何來的父子容器之說?別急,這個在初始化該上文時就明白了。但是這裏的初始化入口在哪呢?沒有看到任何監聽器的創建和調用。實際上這裏的上下文對象初始化是在Servlet初始化時實現的,即init方法,直接來到HttpServletBean的init方法(分析SpringMVC源碼時講過):

    	public final void init() throws ServletException {
    		...省略
    		
    		// Let subclasses do whatever initialization they like.
    		initServletBean();
    	}
    
    	protected final void initServletBean() throws ServletException {
    		try {
    			this.webApplicationContext = initWebApplicationContext();
    			initFrameworkServlet();
    		}
    	}
    
    	protected WebApplicationContext initWebApplicationContext() {
    		//這裡會從servletContext中獲取到父容器,就是通過監聽器加載的容器
    		WebApplicationContext rootContext =
    				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    		WebApplicationContext wac = null;
    
    		if (this.webApplicationContext != null) {
    			// A context instance was injected at construction time -> use it
    			wac = this.webApplicationContext;
    			if (wac instanceof ConfigurableWebApplicationContext) {
    				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
    				if (!cwac.isActive()) {
    					if (cwac.getParent() == null) {
    						cwac.setParent(rootContext);
    					}
    					//容器加載
    					configureAndRefreshWebApplicationContext(cwac);
    				}
    			}
    		}
    		if (wac == null) {
    			wac = findWebApplicationContext();
    		}
    		if (wac == null) {
    			wac = createWebApplicationContext(rootContext);
    		}
    
    		if (!this.refreshEventReceived) {
    			synchronized (this.onRefreshMonitor) {
    				onRefresh(wac);
    			}
    		}
    
    		if (this.publishContext) {
    			// Publish the context as a servlet context attribute.
    			String attrName = getServletContextAttributeName();
    			getServletContext().setAttribute(attrName, wac);
    		}
    
    		return wac;
    	}
    

    看到這裏想你也應該明白了,首先從ServletContext中拿到父容器,然後設置到當前容器的parent中,實現了父子容器的組織,而這樣設計好處我想也是很清楚的,子容器目前裝載的都是MVC的配置和Bean,簡單點說就是Controller,父容器中都是Service,Controller是依賴於Service的,如果不構建這樣的層級關係並優先實例化父容器,你怎麼實現Controller層的依賴注入成功呢?

    總結

    本篇結合之前的文章,分析了SpringMVC零XML配置的實現原理,也補充了之前未分析到父子容器關係,讓我們能從細節上更加全面的理解SpringIOC的實現原理,相信看完本篇對於SpringBoot的實現你也會有自己的想法。

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準