2012-07-26 163 views
-1

我有以下代碼:使用多線程的線程池

private static void SplitTilesRecursive(Image original, int level) 
    { 
     int mapWidth = GetMapWidth(level); 
     int tilesOnSide = mapWidth/TileSize; 


     using (Image resized = ResizeImage(original, new Size(mapWidth, mapWidth))) 
     { 
      for (int x = 0; x < tilesOnSide; x++) 
       for (int y = 0; y < tilesOnSide; y++) 
       { 
        CropAndSaveTile(resized, x, y, level); 
       } 
     } 


     if (level > 0) 
      SplitTilesRecursive(original, level - 1); 
    } 

    private static void CropAndSaveTile(Image image, int x, int y, int level) 
    { 
     var info = (CropInfo) o; 
     var cropArea = new Rectangle(x * TileSize, y * TileSize, TileSize, TileSize); 


     using (var bmpImage = new Bitmap(image)) 
     using (Bitmap bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat)) 
     { 
      string filename = String.Format(TileFilename, level, x, y); 

      // the Portable Network Graphics (PNG) encoder is used implicitly 
      bmpCrop.Save(Path.Combine(OutputDir, filename)); 
      Console.WriteLine("Processed " + filename); 
     } 
    } 

的方法CropAndSaveTile需要一段時間,所以我想拆分任務關閉使用線程池一個新的線程。我試圖用Task.Factory.StartNew來完成這個。問題是我需要將這4個參數傳遞給線程,所以我必須創建一個可以投射到對象的類。

private class CropInfo 
    { 
     public CropInfo(Image image, int x, int y, int level) 
     { 
      Image = image; 
      X = x; 
      Y = y; 
      Level = level; 
     } 

     public Image Image { get; set; } 
     public int X { get; set; } 
     public int Y { get; set; } 
     public int Level { get; set; } 
    } 

    private static void SplitTilesRecursive(Image original, int level) 
    { 
     // ... 
     using (Image resized = ResizeImage(original, new Size(mapWidth, mapWidth))) 
     { 
      for (int x = 0; x < tilesOnSide; x++) 
       for (int y = 0; y < tilesOnSide; y++) 
       { 
        Task.Factory.StartNew(CropAndSaveTile, new CropInfo(resized, x, y, level)); 
       } 
     } 
     // ... 
    } 

    private static void CropAndSaveTile(object o) 
    { 
     var info = (CropInfo) o; 
     // ... 
    } 

這幾乎奏效。問題是new Bitmap(info.Image)會拋出一個ArgumentException(參數無效)。我已經測試了這個而不使用Task.Factory.StartNew,而是直接使用CropAndSaveTile(new CropInfo(resized, x, y, level));來調用該方法,它工作正常。 StartNew和線程運行之間發生了一些事情。這可能是由於SplitTilesRecursive結束循環以及resized被處置時造成的同步問題嗎?如果不是,我怎樣才能正確地將多個參數傳遞給線程來作爲線程池的一部分?

+0

你是否對所有任務使用'resized'的相同實例? – Tudor 2012-07-26 15:56:33

回答

2

爲什麼你需要創建一個類?你可以這樣做:

Task.Factory.StartNew(()=>CropandSaveTile(resized, y, y, level)); 

該語言將爲您創建一個類作爲「閉包」。

+0

我擔心閉包會導致問題,例如,如果在線程執行之前x或y發生了變化。 – JohnD 2012-07-26 15:27:11

+0

封閉程序的功能與您要做的是相同的。它會將x和y的值複製到編譯器生成的類的成員中。唯一的問題是如果你修改了參考變量數據。即如果您更改了圖像引用的內存。但是,如果您手動創建課程,則會遇到同樣的問題 – 2012-07-26 15:29:40

+0

好的,但我問的是如何執行此操作,以免遇到同樣的問題?我以爲通過使用類遠離了這種情況,但是我忽略了這樣一個事實,即該對象不會被複制到實例中,而是被引用。 – JohnD 2012-07-26 15:31:29

2

嘗試使用的xy本地副本內循環:

for (int x = 0; x < tilesOnSide; x++) 
    for (int y = 0; y < tilesOnSide; y++) 
    { 
     int x1 = x; 
     int y1 = y; 
     Task.Factory.StartNew(() => CropAndSaveTile(resized, x1, y1, level)); 
    } 

這保證了每個拉姆達看到一對單獨的xy值。