如何使Parallel.For按顺序排队迭代

时间:2019-04-06 11:26:43

标签: c# .net task-parallel-library

我有一个for循环。平行的。和很多迭代。我想利用环境中的所有处理器核心。但是我确实需要将迭代顺序地排队。

例如,我有100个Parallel.For迭代可运行,并且有4个内核。似乎要运行的第一个迭代为0、25、50、75。我想要的是让它们成为第一,第二,第三和第四,当其中一个完成时,下一个应该是第五,然后是第六,依此类推。

当我弄乱“并行度”参数时,它只是将迭代范围划分为不同的值,例如0th,10th,20th等。我可以实现顺序排队的唯一方法是将并行度设置为等于全部迭代次数,这样它们至少会顺序开始。但是我认为这不是最有效的方法,因为多余的过程会减慢以前启动的过程的速度。

那么我如何让Parallel.for队列按顺序开始,但一次不超过指定次数?

2 个答案:

答案 0 :(得分:0)

如果需要特殊订单,可以使用yarn install来确保先进先出:

#!/usr/bin/env node
const path = require('path');
const yargs = require('yargs');
const execa = require('execa');
const jsonfile = require('jsonfile');

const noop = () => {};

async function lernaCommand(command, options) {
  const { devBranch } = options;
  const branch = await getCurrentBranch();
  if (branch !== devBranch) {
    return Promise.reject(
      `You should be in "${devBranch}" branch to detect changes but current branch is "${branch}".`
    );
  }
  const latestVersion = await getLatestVersion();

  const bumpVersion = async bump => {
    await lernaVersion(latestVersion, bump);
    const version = await getLernaVersion();
    const packageJsonPath = path.resolve(__dirname, 'package.json');
    const packageJson = await jsonfile.readFile(packageJsonPath);
    packageJson.version = version;
    await jsonfile.writeFile(packageJsonPath, packageJson, { spaces: 2 });
    await exec('git', ['add', '-A']);
    await exec('git', ['commit', '-m', 'Version bump.']);
    return version;
  };

  const reject = e => {
    if (typeof e === 'string') {
      return Promise.reject(e);
    }
    return Promise.reject('Unable to detect any changes in packages, probably nothing has changed.');
  };

  switch (command) {
    case 'publish': {
      const { bump, skipVersion, releaseBranch } = options;
      if (releaseBranch === devBranch) {
        return Promise.reject('Release and development branches can\'t be the same.');
      }
      try {
        const version = skipVersion ? await getLernaVersion() : await bumpVersion(bump);
        await lernaPublish(latestVersion, version);
        await exec('git', ['checkout', releaseBranch]);
        await exec('git', ['merge', '--no-ff', devBranch, '-m', `Version ${version}.`]);
        await exec('git', ['tag', '-a', version, '-m', `Version ${version}.`]);
        await exec('git', ['checkout', devBranch]);
      }
      catch (e) {
        return reject(e);
      }
      break;
    }

    case 'version': {
      const { bump } = options;
      try {
        await bumpVersion(bump);
      }
      catch (e) {
        return reject(e);
      }
      break;
    }

    case 'changed': {
      try {
        await lernaChanged(latestVersion);
      }
      catch (e) {
        return reject(e);
      }
      break;
    }
  }
}

async function lernaPublish(since, version) {
  if (since === version) {
    return Promise.reject(`Unable to publish packages with same version ${version}.`);
  }
  return exec('lerna', ['publish', '--since', since, version, '--no-push', '--no-git-tag-version', '--yes']);
}

async function lernaVersion(since, bump) {
  return exec('lerna', ['version', '--since', since, bump, '--no-push', '--no-git-tag-version', '--yes']);
}

async function lernaChanged(since) {
  return exec('lerna', ['changed', '--since', since]);
}

async function patch() {
  try {
    await exec('git', ['apply', '-p3', '--directory', 'node_modules/@lerna/version', 'lerna-version-since.patch']);
  }
  catch (e) {
    return Promise.reject('Lerna Gitflow patch is not applied (probably, it\'s already applied before).');
  }
}

async function getCurrentBranch() {
  const { stdout } = await exec('git', ['branch']);
  const match = stdout.match(/\* ([\S]+)/);
  if (match === null) {
    return Promise.reject('Unable to detect current git branch.');
  }
  return match[1];
}

async function getLatestTaggedCommit() {
  const { stdout } = await exec('git', ['rev-list', '--tags', '--max-count', 1]);
  if (!stdout) {
    return Promise.reject('Unable to find any tagged commit.');
  }
  return stdout;
}

async function getLatestVersion() {
  const commit = await getLatestTaggedCommit();
  const { stdout } = await exec('git', ['describe', '--tags', commit]);
  return stdout;
}

async function getLernaVersion() {
  const lernaJson = await jsonfile.readFile(path.resolve(__dirname, 'lerna.json'));
  return lernaJson.version;
}

function exec(cmd, args, opts) {
  console.log(`$ ${cmd} ${args.join(' ')}`);
  const promise = execa(cmd, args, opts);
  promise.stdout.pipe(process.stdout);
  promise.stderr.pipe(process.stderr);
  return promise;
}

yargs
  .wrap(null)
  .strict(true)
  .help(true, 'Show help')
  .version(false)
  .fail((msg, error) => {
    console.error(error);
    if (msg) {
      console.error(msg);
    }
  })
  .demandCommand()
  .command(
    'publish <bump>',
    'Bump and commit packages\' in development branch, then publish, merge into and tag in release branch',
    yargs => yargs
      .positional('bump', {
        describe: 'Type of version update',
        type: 'string'
      })
      .option('skip-version', {
        describe: 'Skip version bumping and commiting in development branch',
        type: 'boolean',
        default: false
      }),
    opts => lernaCommand('publish', opts)
  )
  .command(
    'version <bump>',
    'Bump and commit packages\' version in development branch',
    yargs => yargs
      .positional('bump', {
        describe: 'Type of version update',
        type: 'string'
      }),
    opts => lernaCommand('version', opts)
  )
  .command(
    'changes',
    'Detect packages changes since latest release',
    noop,
    opts => lernaCommand('changed', opts)
  )
  .command('patch', 'Patch Lerna to use with Gitflow', noop, () => patch())
  .options({
    'dev-branch': {
      describe: 'Name of git development branch',
      type: 'string',
      demandOption: true,
      default: 'develop'
    },
    'release-branch': {
      describe: 'Name of git release branch',
      type: 'string',
      demandOption: true,
      default: 'master'
    }
  })
  .parse();

看看输出。如果两个线程并行开始,则您不知道顺序。但是通常下一个会出队。

答案 1 :(得分:0)

这可能有点麻烦,但我只是在这里想。最好的情况是对问题采取不同的看法,最坏的是投反对票。 :(

您可以先拆分这些值以对其进行操作,例如:

public Dictionary<string, List<int>> CreateDictionary()
{
    var rtnValue = new Dictionary<string, List<int>>()
    {
        { "0", new List<int>() },
        { "1", new List<int>() },
        { "2", new List<int>() },
        { "3", new List<int>() }
    };


    var rando = new Random();
    for (int i = 0; i < 100; i++)
    {
        if (i < 25)
            rtnValue["0"].Add(rando.Next(-100, 100));

        if (i > 24 && i < 50)
            rtnValue["1"].Add(rando.Next(-100, 100));

        if (i > 49 && i < 75)
            rtnValue["2"].Add(rando.Next(-100, 100));

        if (i > 74 && i < 100)
            rtnValue["3"].Add(rando.Next(-100, 100));
    }

    return rtnValue;
}

现在让我们有一个可以完成工作的方法。

public static string processList(IList<int> param)
{
    return string.Join(", ", param);
}

最后像下面这样并行进行工作:

public void runInParallel()
{
    var DataToOperateOn = CreateDictionary();
    Parallel.For(0, DataToOperateOn.Count, i =>
    {
        processList(DataToOperateOn[i.ToString()]);
    });
}

或者,您可以使用Parallel.Invoke来完成几乎相同的事情:

public void runInParallel()
{
    var DataToOperateOn = CreateDictionary();
    Parallel.Invoke(
    () => { processList(DataToOperateOn["0"]); },
    () => { processList(DataToOperateOn["1"]); },
    () => { processList(DataToOperateOn["2"]); },
    () => { processList(DataToOperateOn["3"]); });
}