Alink漫談(七) : 如何劃分訓練數據集和測試數據集

Alink漫談(七) : 如何劃分訓練數據集和測試數據集

目錄

  • Alink漫談(七) : 如何劃分訓練數據集和測試數據集
    • 0x00 摘要
    • 0x01 訓練數據集和測試數據集
    • 0x02 Alink示例代碼
    • 0x03 批處理
      • 3.1 得到記錄數
      • 3.2 隨機選取記錄
        • 3.2.1 得到總記錄數
        • 3.2.2 決定每個task選擇記錄數
        • 3.2.3 每個task選擇記錄
      • 3.3 設置訓練數據集和測試數據集
    • 0x04 流處理
    • 0x05 參考

0x00 摘要

Alink 是阿里巴巴基於實時計算引擎 Flink 研發的新一代機器學習算法平台,是業界首個同時支持批式算法、流式算法的機器學習平台。本文將為大家展現Alink如何劃分訓練數據集和測試數據集。

0x01 訓練數據集和測試數據集

兩分法

一般做預測分析時,會將數據分為兩大部分。一部分是訓練數據,用於構建模型,一部分是測試數據,用於檢驗模型。

三分法

但有時候模型的構建過程中也需要檢驗模型/輔助模型構建,這時會將訓練數據再分為兩個部分:1)訓練數據;2)驗證數據(Validation Data)。所以這種情況下會把數據分為三部分。

  • 訓練數據(Train Data):用於模型構建。
  • 驗證數據(Validation Data):可選,用於輔助模型構建,可以重複使用。
  • 測試數據(Test Data):用於檢測模型構建,此數據只在模型檢驗時使用,用於評估模型的準確率。絕對不允許用於模型構建過程,否則會導致過渡擬合。

Training set是用來訓練模型或確定模型參數的,如ANN中權值等;

Validation set是用來做模型選擇(model selection),即做模型的最終優化及確定,如ANN的結構;

Test set則純粹是為了測試已經訓練好的模型的推廣能力。當然test set並不能保證模型的正確性,他只是說相似的數據用此模型會得出相似的結果。

實際應用

實際應用中,一般只將數據集分成兩類,即training set 和test set,大多數文章並不涉及validation set。我們這裏也不涉及。大家常用的sklearn的train_test_split函數就是將矩陣隨機劃分為訓練子集和測試子集,並返回劃分好的訓練集測試集樣本和訓練集測試集標籤。

0x02 Alink示例代碼

首先我們給出示例代碼,然後會深入剖析:

public class SplitExample {
  public static void main(String[] args) throws Exception {
    String url = "iris.csv";
    String schema = "sepal_length double, sepal_width double, petal_length double, petal_width double, category string";

    //這裡是批處理
    BatchOperator data = new CsvSourceBatchOp().setFilePath(url).setSchemaStr(schema);
    SplitBatchOp spliter = new SplitBatchOp().setFraction(0.8);
    spliter.linkFrom(data);
    BatchOperator trainData = spliter;
    BatchOperator testData = spliter.getSideOutput(0);

    // 這裡是流處理
    CsvSourceStreamOp dataS = new CsvSourceStreamOp().setFilePath(url).setSchemaStr(schema);
    SplitStreamOp spliterS = new SplitStreamOp().setFraction(0.4);
    spliterS.linkFrom(dataS);
    StreamOperator train_data = spliterS;
    StreamOperator test_data = spliterS.getSideOutput(0);
  }
}

0x03 批處理

SplitBatchOp是分割批處理的主要類,具體構建DAG的工作是在其linkFrom完成的。

總體思路比較簡單:

  1. 假定有一個採樣比例 fraction
  2. 將數據集分區,并行計算每個分區上的記錄數
  3. 把每個分區上的記錄數累積,得到所有記錄總數 totCount
  4. 從上而下計算出一個採樣總數:numTarget = totCount * fraction
  5. 因為具體選擇元素是在每個分區上做的,所以在每個分區上,分別計算出來這個分區應該採樣的記錄數,比如第n個分區上應採樣記錄數:task_n_count * fraction
  6. 把這些分區 “應該採樣的記錄數” 累積,得出來從下而上計算出的採樣總數: totSelect = task_1_count * fraction + task_2_count * fraction + ... task_n_count * fraction
  7. numTarget 和 totSelect 可能不相等,所以隨機決定把多出來的 numTarget - totSelect 加入到某一個task中。
  8. 在每個task上採樣得到具體的記錄。

3.1 得到記錄數

如果要分割數據,首先必須知道數據集的記錄數。比如這個DataSet的記錄是1萬個?還是十萬個?因為數據集可能會很大,所以這一步操作也使用了并行處理,即把數據分區,然後通過mapPartition操作得到每一個分區上元素的數目。

DataSet<Tuple2<Integer, Long>> countsPerPartition = DataSetUtils.countElementsPerPartition(rows); //返回哪個task有哪些記錄數

DataSet<long[]> numPickedPerPartition = countsPerPartition
    .mapPartition(new CountInPartition(fraction)) //計算總數
    .setParallelism(1)
    .name("decide_count_of_each_partition");

因為每個分區就對應了一個task,所以我們也可以認為,這是獲取了每個task的記錄數。

具體工作是在 DataSetUtils.countElementsPerPartition 中完成的。返回類型是<index of this subtask, record count in this subtask>,比如3號task擁有30個記錄。

public static <T> DataSet<Tuple2<Integer, Long>> countElementsPerPartition(DataSet<T> input) {
   return input.mapPartition(new RichMapPartitionFunction<T, Tuple2<Integer, Long>>() {
      @Override
      public void mapPartition(Iterable<T> values, Collector<Tuple2<Integer, Long>> out) throws Exception {
         long counter = 0;
         for (T value : values) {
            counter++; //計算本task的記錄總數
         }
         out.collect(new Tuple2<>(getRuntimeContext().getIndexOfThisSubtask(), counter));
      }
   });
}

計算總數的工作其實是在下一階段算子中完成的。

3.2 隨機選取記錄

接下來的工作主要是在 CountInPartition.mapPartition 完成的,其作用是隨機決定每個task選擇多少個記錄。

這時候就不需要并行了,所以 .setParallelism(1)

3.2.1 得到總記錄數

得到了每個分區記錄數之後,我們遍歷每個task的記錄數,然後累積得到總記錄數 totCount(就是從上而下計算出來的總數)。

public void mapPartition(Iterable<Tuple2<Integer, Long>> values, Collector<long[]> out) throws Exception {
	long totCount = 0L;
	List<Tuple2<Integer, Long>> buffer = new ArrayList<>();
	for (Tuple2<Integer, Long> value : values) { //遍歷輸入的所有分區記錄
    totCount += value.f1; //f1是Long類型的記錄數
    buffer.add(value);
	}
  ...
  //後續代碼在下面分析。  
}

3.2.2 決定每個task選擇記錄數

然後CountInPartition.mapPartition函數中會隨機決定每個task會選擇的記錄數。mapPartition的參數 Iterable<Tuple2<Integer, Long>> values 就是前一階段的結果 :一個元祖<task id, 每個task的記錄數目>。

把這些元祖結合在一起,記錄在buffer這個列表中。

buffer = {ArrayList@8972}  size = 4
 0 = {Tuple2@8975} "(3,38)" // 3號task,其對應的partition記錄數是38個。
 1 = {Tuple2@8976} "(2,0)"
 2 = {Tuple2@8977} "(0,38)"
 3 = {Tuple2@8978} "(1,74)"

系統的task數目就是buffer大小。

int npart = buffer.size(); // num tasks

然後,根據”記錄總數“計算出來 “隨機訓練數據的個數numTarget”。比如總數1萬,應該隨機分配20%,於是numTarget就應該是2千。這個数字以後會用到。

long numTarget = Math.round((totCount * fraction));

得到每個task的記錄數目,比如是上面buffer中的 38,0,38,還是74,記錄在 eachCount 中。

for (Tuple2<Integer, Long> value : buffer) {
    eachCount[value.f0] = value.f1;
}

得到每個task中隨機選中的訓練記錄數,記錄在 eachSelect 中。就是每個task目前 “記錄数字 * fraction”。比如3號task記錄數是38個,應該選20%,則38*20%=8個。

然後把這些task自己的“隨機訓練記錄數”再累加起來得到 totSelect(就是從下而上計算出來的總數)。

long totSelect = 0L;
for (int i = 0; i < npart; i++) {
    eachSelect[i] = Math.round(Math.floor(eachCount[i] * fraction));
    totSelect += eachSelect[i];
}

請注意,這時候 totSelect 和 之前計算的numTarget就有具體細微出入了,就是理論上的一個数字,但是我們 從上而下 計算 和 從下而上 計算,其結果可能不一樣。通過下面我們可以看出來。

numTarget = all count * fraction

totSelect = task_1_count * fraction + task_2_count * fraction + ...

所以我們下一步要處理這個細微出入,就得到remain,這是”總體算出來的隨機數目” numTarget 和 “從所有task選中的隨機訓練記錄數累積” totSelect 的差。

if (totSelect < numTarget) {
    long remain = numTarget - totSelect;
    remain = Math.min(remain, totCount - totSelect);

如果剛好個數相等,則就正常分配。

if (remain == totCount - totSelect) {

如果數目不等,隨機決定把”多出來的remain”加入到eachSelect數組中的隨便一個記錄上。

for (int i = 0; i < Math.min(remain, npart); i++) {
    int taskId = shuffle.get(i);
    while (eachSelect[taskId] >= eachCount[taskId]) {
          taskId = (taskId + 1) % npart;
    }
    eachSelect[taskId]++;
}

最後給出所有信息

long[] statistics = new long[npart * 2];
for (int i = 0; i < npart; i++) {
    statistics[i] = eachCount[i];
    statistics[i + npart] = eachSelect[i];
}
out.collect(statistics);

// 我們這裡是4核,所以前面四項是eachCount,後面是eachSelect
statistics = {long[8]@9003} 
 0 = 38 //eachCount
 1 = 38
 2 = 36
 3 = 38
   
 4 = 31 //eachSelect
 5 = 31
 6 = 28
 7 = 30

這些信息是作為廣播變量存儲起來的,馬上下面就會用到。

 .withBroadcastSet(numPickedPerPartition, "counts")

3.2.3 每個task選擇記錄

CountInPartition.PickInPartition函數中會隨機在每個task選擇記錄。

首先得到task數目 和 之前存儲的廣播變量(就是之前剛剛存儲的)。

int npart = getRuntimeContext().getNumberOfParallelSubtasks();
List<long[]> bc = getRuntimeContext().getBroadcastVariable("counts");

分離count和select。

long[] eachCount = Arrays.copyOfRange(bc.get(0), 0, npart);
long[] eachSelect = Arrays.copyOfRange(bc.get(0), npart, npart * 2);

得到總task數目

int taskId = getRuntimeContext().getIndexOfThisSubtask();

得到自己 task 對應的 count, select

long count = eachCount[taskId];
long select = eachSelect[taskId];

添加本task對應的記錄,隨機洗牌打亂順序

for (int i = 0; i < count; i++) {
     shuffle.add(i); //就是把count內的数字加到數組
}
Collections.shuffle(shuffle, new Random(taskId)); //洗牌打亂順序

// suffle舉例
shuffle = {ArrayList@8987}  size = 38
 0 = {Integer@8994} 17
 1 = {Integer@8995} 8
 2 = {Integer@8996} 33
 3 = {Integer@8997} 34
 4 = {Integer@8998} 20
 5 = {Integer@8999} 0
 6 = {Integer@9000} 26
 7 = {Integer@9001} 27
 8 = {Integer@9002} 23
 9 = {Integer@9003} 28
 10 = {Integer@9004} 9
 11 = {Integer@9005} 16
 12 = {Integer@9006} 13
 13 = {Integer@9007} 2
 14 = {Integer@9008} 5
 15 = {Integer@9009} 31
 16 = {Integer@9010} 15
 17 = {Integer@9011} 22
 18 = {Integer@9012} 18
 19 = {Integer@9013} 35
 20 = {Integer@9014} 36
 21 = {Integer@9015} 12
 22 = {Integer@9016} 7
 23 = {Integer@9017} 21
 24 = {Integer@9018} 14
 25 = {Integer@9019} 1
 26 = {Integer@9020} 10
 27 = {Integer@9021} 30
 28 = {Integer@9022} 29
 29 = {Integer@9023} 19
 30 = {Integer@9024} 25
 31 = {Integer@9025} 32
 32 = {Integer@9026} 37
 33 = {Integer@9027} 4
 34 = {Integer@9028} 11
 35 = {Integer@9029} 6
 36 = {Integer@9030} 3
 37 = {Integer@9031} 24

隨機選擇,把選擇后的再排序回來

for (int i = 0; i < select; i++) {
    selected[i] = shuffle.get(i); //這時候select看起來是按照順序選擇,但是實際上suffle裏面已經是亂序
}
Arrays.sort(selected); //這次再排序

// selected舉例,一共30個
selected = {int[30]@8991} 
 0 = 0
 1 = 1
 2 = 2
 3 = 5
 4 = 7
 5 = 8
 6 = 9
 7 = 10
 8 = 12
 9 = 13
 10 = 14
 11 = 15
 12 = 16
 13 = 17
 14 = 18
 15 = 19
 16 = 20
 17 = 21
 18 = 22
 19 = 23
 20 = 26
 21 = 27
 22 = 28
 23 = 29
 24 = 30
 25 = 31
 26 = 33
 27 = 34
 28 = 35
 29 = 36

發送選擇的數據

if (numEmits < selected.length && iRow == selected[numEmits]) {
    out.collect(row);
    numEmits++;
}

3.3 設置訓練數據集和測試數據集

output是訓練數據集,SideOutput是測試數據集。因為這兩個數據集在Alink內部都是Table類型,所以直接使用了SQL算子 minusAll 來完成分割。

this.setOutput(out, in.getSchema());
this.setSideOutputTables(new Table[]{in.getOutputTable().minusAll(this.getOutputTable())});

0x04 流處理

訓練是在SplitStreamOp類完成的,其通過linkFrom完成了模型的構建。

流處理依賴SplitStream 和 SelectTransformation 這兩個類來完成分割流。具體並沒有建立一個物理操作,而只是影響了上游算子如何與下游算子聯繫,如何選擇記錄。

SplitStream <Row> splited = in.getDataStream().split(new RandomSelectorOp(getFraction()));

首先,用RandomSelectorOp來隨機決定輸出時候選擇哪個流。我們可以看到,這裏就是隨便起了”a”, “b” 這兩個名字而已。

class RandomSelectorOp implements OutputSelector <Row> {
   private double fraction;
   private Random random = null;
   @Override
   public Iterable <String> select(Row value) {
      if (null == random) {
         random = new Random(System.currentTimeMillis());
      }
      List <String> output = new ArrayList <String>(1);
      output.add((random.nextDouble() < fraction ? "a" : "b")); //隨機選取数字分配,隨意起的名字
      return output;
   }
}

其次,得到那兩個隨機生成的流。

DataStream <Row> partA = splited.select("a");
DataStream <Row> partB = splited.select("b");

最後把這兩個流分別設置為output和sideOutput。

this.setOutput(partA, in.getSchema()); //訓練集
this.setSideOutputTables(new Table[]{
DataStreamConversionUtil.toTable(getMLEnvironmentId(), partB, in.getSchema())}); //驗證集

最後返回本身,這時候SplitStreamOp擁有兩個成員變量:

this.output就是訓練集。

this.sideOutPut就是驗證集。

return this;

0x05 參考

訓練數據,驗證數據和測試數據分析

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

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

武肺後經濟復甦關鍵 非洲11國部長籌資造「綠色長城」

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

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

【其他文章推薦】

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

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

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

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

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

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

月銷25000!10萬起要買高B格的國產SUV就看它了~

推薦理由:榮威RX5以車聯網為賣點,推薦配置比較豐富的兩驅自動互聯網智惠版,帶有升級的汽車智能係統2。舒適配置應有盡有,全景天窗、無鑰匙進入/無鑰匙啟動,還有前排座椅加熱,照顧到北方消費者的需求。在自主SUV當中,榮威RX5是一款實力非常強的車型,無論是外觀還是內飾的設計都是比較優秀的,甚至比一些合資SUV還要好,18款對配置進行了改進升級,相信會適合更多消費者。

國產SUV界的網紅SUV,“15萬王”榮威RX5,9月份售出了2萬5台,成績相當不錯。而在近日,其2018款已經上市,2018款車型外觀和造型和現款一樣沒有改動,主要是針對車型和配置進行了升級,新款車型總共12款車型,新增了3款車型,另外兩款車型配置也有提升,這款車究竟如何呢?請看介紹。

推薦理由:榮威RX5以車聯網為賣點,推薦配置比較豐富的兩驅自動互聯網智惠版,帶有升級的汽車智能係統2.0;舒適配置應有盡有,全景天窗、無鑰匙進入/無鑰匙啟動,還有前排座椅加熱,照顧到北方消費者的需求。

在自主SUV當中,榮威RX5是一款實力非常強的車型,無論是外觀還是內飾的設計都是比較優秀的,甚至比一些合資SUV還要好,18款對配置進行了改進升級,相信會適合更多消費者。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

8款老闆愛的車最低只要4萬

還有就是形象與同級比起來要更顯莊重,即使去談大買賣,也不至於讓人瞧不起。其實不僅是別克GL8,還有許多定位為商務MpV的車型同樣也充滿了濃郁的商務氣息,而這次推薦GL8主要原因還是因為它空間及乘坐舒適性達到同級一流水平,應有的电子設備都有,相對而言,GL8是現在市面上主流MpV中比較卓越的一款車型。

關於#老闆開什麼車#這樣的話題

相信不少人都有關注過吧

雖然老闆的座駕並沒有什麼標準

可是總有幾款讓人印象深刻

為什麼老闆們都這麼喜歡這些車?

老闆們喜歡的這些車是否適合你?

開上這些車后你是否就能當老闆?

看完這8款老闆車你就會有答案了

首先要排除一個誤區,誰說老闆就一定要開個大奔馳或大賓利的?開五菱宏光的小賣鋪老闆不也是老闆嗎?什麼空間大、油耗低、動力足、操控好、保養便宜、可靠性高等等優點就不多說了,反正銷量說明一切嘛。不過買最便宜的車裝最多的貨,這些簡單的商業意識還是要具備的。

其實能“裝”的確是五菱宏光的核心價值吧,裝人:最多可放置8個座椅,什麼7座SUV都是辣雞;裝貨:只要裝得下,世界就是你的;裝X:方式太多,請自行上論壇選,總有一款適合你。

可能這隻是我的個人印象吧,從小到大,身邊總有一些開着捷達,成天到處跑業務的創業老闆,他們總是會稱讚自己的捷達很省心,很省油,又好開,說這樣的一台車可以完全滿足他們的代步需求,而且外觀低調,卻很有商務范。

其實說到捷達的外觀,雖然現款的捷達也很低調,也很有商務范,同樣也是個“老實”形象,還是以前方頭方腦的造型給我的印象更深刻,可是那個造型放在今天馬路上,也許展現出更多的是“經典”或“復古”吧。

為什麼說開帕薩特的老闆最神秘?首先,一般人根本分不清帕薩特和輝騰,還有就是的確有許多隱形富豪為了低調會選擇帕薩特這款車,首先大眾標就夠低調了,再來就是外觀造型,家族式設計遍布全車身,讓人怎麼看都覺得是一輛不超過20萬的車。

可是20萬要買低調的車選擇很多,為什麼這些隱形富豪要選帕薩特?其實還是圍繞着舒適性吧,德系紮實底盤質感,大空間;還有就是形象與同級比起來要更顯莊重,即使去談大買賣,也不至於讓人瞧不起。

其實不僅是別克GL8,還有許多定位為商務MpV的車型同樣也充滿了濃郁的商務氣息,而這次推薦GL8主要原因還是因為它空間及乘坐舒適性達到同級一流水平,應有的电子設備都有,相對而言,GL8是現在市面上主流MpV中比較卓越的一款車型。

當然,這樣的MpV一般都是公務用車,老闆一般只坐在第二排,坐在舒適的座椅上,有公事要聊的時候,跟助理溝通起來十分方便;沒事聊的時候,放下靠背,也可以睡得很舒服。

作為豐田越野神車蘭德酷路澤的分支車型,普拉多其實已經更傾向於都市SUV,可是在造型方面還是很有越野的味道,這也是許多老闆們喜歡它原因之一,不過實際普拉多還是延續了較好的越野性能。

很多人都說什麼樣的人就會開什麼樣的車,我通過普拉多的車主們就能明顯體現出這點,說不上個個都高大威猛,可是至少也是條硬漢。

雖然說“虎頭奔之後再也沒有S級”,有情懷的車迷都喜歡那個年代的S級,可是現在的S級無疑是更好的S級,動力更好,配置更高,價格更低。雖然造型方面的確是向現在的消費市場妥協了,可是現在的S級仍然是D級車裡最有老闆范的。

但是實際上並不是所有買S級的老闆都會請司機,或者說不是所有買了S級的老闆自己都不開,即使自己開S級的時候的確有點像司機,可是他們還是很願意去駕駛這輛高級車。

與其說開霸道(普拉多)的老闆真霸氣,還不如說開攬勝的人才是真正的“霸道總裁”,說到高大上,縱觀市面上所有高端中大型SUV,好像真沒幾款能媲美路虎攬勝的了。也有人說路虎攬勝根本沒有直接的競爭對手,因為在公路和越野的性能都如此卓越,內飾如此豪華,外觀如此霸氣的SUV屈指可數。

雖然說攬勝也有極高的越野性能,可是車主們更願意把它看作是豪華都市SUV,而且攬勝的形象也似乎更合適出現在都市中。

看到勞斯萊斯,貌似我們的話題就要結束了,我也都相信大家和我一樣,勞斯萊斯才是代表最“尊貴”的老闆車,雖然到了這個等級,選擇還有賓利和邁巴赫,可是這些品牌都不如勞斯萊斯尊貴。

其實到了這個級別的車,更講究的應該就奢華、藝術、品味,多的不說了。

不知道大家看了這麼多老闆車之後,有沒有你心儀的那輛呢?或者在你的印象里,什麼車才算老闆車呢?歡迎把你的所有見解評論到下方留意區哦。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

QNX Message Passing,一個讓人頭禿的 IPC BUG

問題描述

QNX系統中 Client 與 Server 通過 QNX Message Passing 進行進程間通信。正式開發前,寫過測試程序 (Client),和 Server 通信一切正常。但把同樣的代碼拷貝到正式的 Client 中,結果發現調用 MsgSend() 后無響應,pidin 显示 Client 處於 REPLY PENDING 的狀態。

幾番嘗試,發現一個讓人很難接受的事實:只能在 Client 的主線程中調用 MsgSend() 才能收到 Server 的回復,如果在 Client 的對等線程中調用MsgSend(),則會因為收不到 Server 的回復而 Pending。

問題分析

測試程序能正常工作,所以起初懷疑是 Client 的問題。面對龐大的 Client,始終沒想到突破口。甚至曾一度懷疑是否是 QNX 的系統限制,MsgSend() 只能在主線程中調用,官網文檔翻了一圈並沒有發現這樣的限制。自己寫了個 IPC Server 來測試,發現並沒有這個問題。

Client Server 結果
測試程序,主線程中調用 MsgSend() 正式 Server OK
正式 Client,對等線程中調用 MsgSend() 正式 Server REPLY PENDING
正式 Client,對等線程中調用 MsgSend() 簡化版測試 Server OK

最後不得不懷疑起 Server,莫非 Server 端有什麼機制能檢測到消息是發自主線程還是對等線程,然後只回復來自主線程的 IPC 請求?

一個典型的 IPC Server 示例代碼如下:

While(1) {
   int rcvid = MsgReceive(chid, &recvBuf, sizeof(recvBuf), NULL);
   /* … process the request based on recvBuf … */
   MsgReply(rcvid, EOK, &replyBuf, sizeof(replyBuf)); // reply to unblock the IPC client
}

如果是從 Client 的主線程發送來的消息,rcvid 是一個很小的数字,如 1, 3, 5… 如果是從Client 的對等線程發送來的消息,rcvid 是一個大於 65535 的数字,如 65538, 65540, 65542… 

讓人意外的是 Server 用了一個 int16_t 來保存 rcvid,直接導致後續的 MsgReply() 無法正確的將消息回給 Client,從而導致 Client 一直處於 REPLY PENDING 狀態。

Reference

  • http://www.qnx.com/developers/docs/7.0.0/#com.qnx.doc.neutrino.sys_arch/topic/ipc_Robust.html  

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

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

不公平的碳足跡:全球前10%富人 25年來碳排放占世界一半

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

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

【其他文章推薦】

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

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

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

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

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

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

不談情懷只談性價比,這台300萬的買菜車是真的強!

採用三電機和3。5L雙渦輪增壓發動機的配合,組成中置引擎四驅系統,最大輸出功率可以達到573匹馬力,3。5秒以內就能完成百公里加速,性能已經媲美現在的主流歐美超跑。而混合動力組成的四驅系統,也能夠獲得較強的彎道性能。

隨着國內的汽車消費水平不斷提高,超跑已經不是可望而不可褻玩的車型,而是變成了更多有實力人士的玩具。而在三百萬以下這個級別,假如你不喜歡Huracan Lp580-2的張揚,又不喜歡R8 V10的功利主義。Mclaren 570s又有點輕佻,保時捷則又顯得不夠入流,而你又對獨一無二更看重的話,你或許會選擇這一台車,Acura NSX。

對於本田來說,NSX這三個字可以說是精神圖騰一般的存在。初代的NSX以打敗法拉利當時的旗艦348為己任,加入了F1的科技,採用全鋁車身和全鋁懸挂,加上VTEC加持的C30A發動機。極高的彎道極限和易於駕駛的特點打開了現代高性能跑車的大門,成為一代經典。

而新一代的NSX,則是Acura現在最先進的混合動力跑車。採用三電機和3.5L雙渦輪增壓發動機的配合,組成中置引擎四驅系統,最大輸出功率可以達到573匹馬力,3.5秒以內就能完成百公里加速,性能已經媲美現在的主流歐美超跑。而混合動力組成的四驅系統,也能夠獲得較強的彎道性能。

另一方面,也繼承了初代NSX那種易於駕駛的特點,甚至還刻意使用一些小技巧來提升行駛品質和舒適性。加上全國每年只有3台配額的身份,足以成為跑車玩家爭相擁有的目標。

而這台NSX究竟開起來怎樣,它究竟能用什麼方法來再一次詮釋“New Sportscar experimental”的核心精神?就請關注這一期的視頻。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

不到40萬就能買4.8秒破百的3廂車,車主卻各種不滿?

發動機:2。0T最大馬力(ps):290最大扭矩(N·m)380變速箱:7DCT驅動方式:前置四驅前/后懸架類型:麥弗遜式獨立懸架/多連桿獨立懸架動力無疑是這款車的核心賣點,4。8秒左右的實測百公里加速時間,對於40萬以下的車型來說,幾乎找不到對手了。

什麼車可以扮豬吃老虎?

或許日系粉們還會想起

EVO、STI那些日系性能車

但是這些車在中國已經極少了

雖然高爾夫R、福克斯RS還能買到

但似乎也沒那麼接地氣了

畢竟許多中國消費者買車講究面子

就算對於性能車

也希望能有個豪華品牌的車標

而且三廂車似乎更符合國人的審美

實際上也更實用

所以像奧迪S3這樣的車型

指導價不到40萬

優惠過後35萬左右

百公里加速4.8秒

還能日常上下班

還能開去市場買車

看看車主到底是什麼感受

長*寬*高:4474*1796*1392mm

軸距:2628mm

車型定位:緊湊型車

外觀方面,奧迪S3基於奧迪A3打造,絕大部分零部件都是共享的,整體外觀造型也是一樣的,只是在細節方面做了一些點綴,比如車身前後的“S3”標識、四齣排氣、銀色的外后視鏡等。而且在車漆方面,也可以選擇S3的專屬車漆“聖邦藍”,整體效果比起奧迪A3個性不少,而且也顯得更精緻了,但也不會太激進,看上去也只能算是一輛買菜車的外觀。

內飾的整體布局跟奧迪A3也基本保持一致,但內飾方面做的點綴比外觀更明顯,處處可見,方向盤、座椅、門踏板等都有“S3”的標識。

空間方面的方面,1.78米的試乘員坐在前排調到最低位置調整好坐姿,頭部還剩餘4指空間,保持前排座椅不變,同一位試乘員坐進後排,頭部空間剩下1指,腿部空間剩下1拳4指。整體表現中規中矩,對於一輛緊湊級的三廂車而言,勉強夠用吧。

發動機:2.0T

最大馬力(ps):290

最大扭矩(N·m)380

變速箱:7DCT

驅動方式:前置四驅

前/后懸架類型:麥弗遜式獨立懸架/多連桿獨立懸架

動力無疑是這款車的核心賣點,4.8秒左右的實測百公里加速時間,對於40萬以下的車型來說,幾乎找不到對手了。而很多車主在體現0-100km/h加速的時候都感到車子起步時,前輪會有小小打滑,隨後四驅系統激活變直接衝出去,什麼推背感應該也不用我多說了。

從車主口碑中可以看出,最滿意的方面幾乎都是外觀或動力,但不滿意的地方都因人而異,各種各樣,所以其實要將奧迪S3看作是一輛動力很強的家用車,在實用性方面還是有些不足的。

優惠幅度各地不同,具體以4S店報價為準。有些車主則說沒太大優惠,不過基本都會有,只是幅度不同而已,但也有車主表示能拿到10W+的優惠,聽起來似乎不那麼靠譜,不過還是值得到店認證,如果真的能30萬不到就買到,那真的賺翻了。

其實在這個價位,要找一輛跟奧迪S3這樣的動力水平,而且還是4門的三廂車,幾乎是沒有的,而且這樣的車型定位在中國也應該有不錯的市場潛力,如果真的可以拿到5W以上的終端優惠,那應該是非常值得的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

C# 9.0 新特性之模式匹配簡化

閱讀本文大概需要 2 分鐘。

記得在 MS Build 2020 大會上,C# 語言開發項目經理 Mads Torgersen 宣稱 C# 9.0 將會隨着 .NET 5 在今年 11 月份正式發布。目前 .NET 5 已經到了 Preview 5 階段了,C# 9.0 也已經初具規模。忍不住激動的心情,暫停更新《C#.NET 拾遺補漏》系列幾天,先要和大家分享一下我了解到的 C# 9.0 的新特性。由於新特性比較多,所以會分成幾篇來講。這是第一篇,專講模式匹配這個特性的簡化。

模式匹配(Pattern Matching)是在 C# 7.0 引入的,是對 switch 語句的增強,可以支持實現複雜的條件匹配。下面我先用一個示例來展示一下模式匹配的一般的用法。

假如現在我們要計算各種車輛在某高速的通行費,比如有下面四種車輛,分別定義為以下四個類,各個類中定義了和通行費計算相關的屬性:

public class Car
{
    public int Passengers { get; set; }
}

public class DeliveryTruck
{
    public int GrossWeightClass { get; set; }
}

public class Taxi
{
    public int Fares { get; set; }
}

public class Bus
{
    public int Capacity { get; set; }
    public int Riders { get; set; }
}

下面用用模式匹配的方式來實現一個計算通行費的方法:

public decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Car { Passengers: 0}        => 2.00m + 0.50m,
    Car { Passengers: 1}        => 2.0m,
    Car { Passengers: 2}        => 2.0m - 0.50m,
    Car c                       => 2.00m - 1.0m,

    Taxi t => t.Fares switch
    {
        0 => 3.50m + 1.00m,
        1 => 3.50m,
        2 => 3.50m - 0.50m,
        _ => 3.50m - 1.00m
    },

    Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
    Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
    Bus b => 5.00m,

    DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
    DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
    DeliveryTruck _ => 10.00m,

    { } => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
    null => throw new ArgumentNullException(nameof(vehicle))
};

代碼來源於文末參考鏈接

如果上面代碼閱讀起來感覺吃力,你可以先閱讀文末參考鏈接中的第一個鏈接,關於模式匹配的詳細介紹。

實現這個業務邏輯,若在 C# 7.0 之前,需要用一堆的 if/else 來實現。有了模式匹配后,變得方便了很多,而且使用上很靈活,代碼結構也更優美。

對我來說,模式匹配是個極好的特性!但這還不夠,C# 9.0 對模式匹配的寫法做了進一步的簡化!

以上面代碼為例,模式匹配可以分為三種:簡單模式、關係模式和邏輯模式。下面分別說說 C# 9.0 對三種模式的簡化。

簡單模式

以上面 CalculateToll 方法示例代碼為例,簡單模式是這種:

vehicle switch
{
    ...
    Car c => 2.00m - 1.0m
}

我們其實可以發現,上面的變量 c 聲明了卻沒用被使用,現在 C# 9.0 中可以把它省略了:

vehicle switch
{
    ...
    Car => 2.00m - 1.0m
}

關係模式

以上面 CalculateToll 方法示例代碼為例,關係模式是通過比較(大小)關係來匹配的,對應的代碼片段如下:

DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
DeliveryTruck _ => 10.00m,

現在 C# 9.0 可以簡寫成:

DeliveryTruck t when t.GrossWeightClass switch
{
    > 5000 => 10.00m + 5.00m,
    < 3000 => 10.00m - 2.00m,
    _ => 10.00m,
}

邏輯模式

在 C# 9.0 中,你可以通過邏輯操作符 and、or 和 not 對模式進行組合,下面是一些示例:

DeliveryTruck t when t.GrossWeightClass switch
{
    < 3000 => 10.00m - 2.00m,
    >= 3000 and <= 5000 => 10.00m,
    > 5000 => 10.00m + 5.00m,
}

not null => throw new ArgumentException($"Not a known vehicle type: {vehicle}", nameof(vehicle)),
null => throw new ArgumentNullException(nameof(vehicle))

另外,not 關鍵字還可以用來替代 if 條件判斷中的邏輯非(!),比如:

// 原來的寫法
if (!(e is Customer)) { ... }

// 新的寫法(易讀性更好)
if (e is not Customer) { ... }

C# 9.0 還有很多其它好用的新特性,下一篇文章繼續與你分享。文章寫短一點不是因為我偷懶哈,而是為了促使大家一次性看完,方便大家在零碎時間閱讀,避免因文章太長而成為“收藏不看”系列。

敬請關注我明天下一篇關於 C# 9.0 新特性的介紹,明天不見不散。

參考:

  1. https://bit.ly/2MNc0DJ
  2. https://bit.ly/2UzEIwu

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

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

【神回復】思域高爾夫怎麼選?20萬內哪款合資SUV更值?

新款捷達的性價比還是挺高的,在市場征戰多年擁有良好的口碑,性價比較高的版本為1。5L手動舒適型與1。5L自動舒適型,兩個配置車型的區別在於變速器不同,在配置上都擁有电子車身穩定系統,后駐車雷達,定速巡航,電動天窗,行車電腦显示屏等,滿足日常家用需求完全沒問題,同時在市場上也有三萬左右的優惠。

如果是自己一個人開的話,那麼在空間方面就不用考慮太多了,高爾夫的話採用的1.4T發動機搭配雙離合的組合,雖然在動力輸出上以及雙離合變速器的平順性上不如思域,但是在轉向的精度上比思域高,換裝多連桿后獨立懸架后底盤更加有韌性,隔音濾振相比思域都有更好的表現。

思域則是擁有非常強的動力輸出,跑高速的話後勁相比高爾夫會更強,同時乘坐空間以及配置要優於高爾夫,搭載的CVT變速器也更加平順,不過就是在轉向,底盤濾振以及隔音上面不如高爾夫。

總的來說,高爾夫的操控性能更佳,思域的動力更強,如果是經常跑高速的話更推薦動力更強的思域。

20萬左右性價比高的SUV可以看看雪佛蘭的探界者或者是斯柯達的柯迪亞克,其中雪佛蘭探界者擁有更低的售價同時加上有可觀的優惠,外觀充滿粗獷之感同時在動力,操控上都有非常不錯的表現,不足在於內飾做工用料一般並且啟停系統無法關閉。

柯迪亞克在華的銷量雖然不溫不火,但是它的產品力同樣十分強大,擁有硬朗的外觀以及非常可觀的乘坐空間,還有七座車型可選,次低配車型的配置已經十分豐富,再加上大眾的成熟動力總成以及良好的底盤濾振和隔音,開起來還是有一定高級感的。

新款捷達的性價比還是挺高的,在市場征戰多年擁有良好的口碑,性價比較高的版本為1.5L手動舒適型與1.5L自動舒適型,兩個配置車型的區別在於變速器不同,在配置上都擁有电子車身穩定系統,后駐車雷達,定速巡航,電動天窗,行車電腦显示屏等,滿足日常家用需求完全沒問題,同時在市場上也有三萬左右的優惠。

假如對空間沒有很大需求的話更加推薦選擇性價比更高的威馳,不僅外觀內飾相比於老氣的經典軒逸更加好看,同時在安全配置上也更加豐富,而經典軒逸的動力相比威馳來說有更強一點的輸出,各種舒適性配置更加齊全,但是售價更高,假如預算充足並且也能接受經典軒逸的外觀內飾的話買來代代步可以可以的,但是預算不是很充足只能買到經典軒逸低配的話建議選擇威馳。

假如是純家用車型的話更加推薦XR-V,雖然其定位為小型SUV,但是在空間上擁有越級的表現,採用的自吸發動機搭配CVT的組合能夠帶來比較平順的體驗,而且在配置方面也能滿足一般的家用需求,性價比頗高,不足之處在於底盤濾振和隔音較差。

馬自達對於運動、操控有着非同一般的偏執,在其SUV車型上也毫不妥協,採用創馳藍天技術+魂動跨界車的風格設計,帶來年輕動感的外觀以及不俗的操控體驗,單憑這兩點就能讓CX-4成為許多年輕消費者的心頭好,不過馬自達在空間方面的造詣一直平平淡淡,適合對操控有追求並且對空間無要求的消費者。

以上就是本期網友問答欄目的全部內容,假如你也想上牆的話,點擊下方留言留下你的問題並且點個贊,就有機會在下期欄目看見你的身影!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案