使用多线程的线程池

问题描述:

我有以下代码:使用多线程的线程池

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

为什么你需要创建一个类?你可以这样做:

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

尝试使用的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值。