在对数据进行多个并发任务的系统中,我想订购任务,以便最少花费等待时间。 系统中的每个任务都使用一组特定的资源,任务按照一定的顺序发出(这个顺序是我想要计算的),一个任务在它可以锁定所有必需的资源之前不会启动。任务按顺序发出,因此第三个任务在第二个任务获得所有锁之前不会启动,依此类推。
Task 1, Resources [A, B]
Task 2, Resources [B, C]
Task 3, Resources [C, D]
Task 4, Resources [E]
Best Solution
Task 1, [A, B]
Task 3, [C, D] //No waiting is possibly required
Task 4, [E] //Put this before task 3, to maximise the distance between uses of the same resource (minimise chances of lock contention)
Task 2, [B, C] //Some waiting *might* be required here
可以使用什么算法来计算最佳排序,以便在使用的资源之间存在最大差距,然后再次使用?
的Nb。这与语言无关,但在C#
中实现的奖励积分答案 0 :(得分:3)
编辑:正如Moron在commmets中指出的那样,给出的目标函数是非线性的。因此,这个答案可以不使用。
一种可能的方法是使用线性编程来解决它。这就是我的想法。如果我们在时间j开始任务i,则引入一个设置为1的决策变量T_i_j(我将计算从0到3的任务)。此外,如果他们需要相同的资源,我们希望“惩罚”彼此接近的调度任务。在给出的示例中,我们希望根据m和n之间的差异来惩罚T0_m和T1_n。我们可以将问题建模如下
Minimize:
3 * T0_0 * T1_1 + 2 * T0_0 * T1_2 + 1 * T0_0 * T1_3
+ 3 * T0_1 * T1_2 + 2 * T0_1 * T1_3
+ 3 * T0_2 * T1_3
+ 3 * T1_0 * T2_1 + 2 * T1_0 * T2_2 + 1 * T1_0 * T2_3
+ 3 * T1_1 * T2_2 + 2 * T1_1 * T2_3
+ 3 * T1_2 * T2_3
Subject to
// We start a task exactly once.
T0_0 + T0_1 + T0_2 + T0_3 = 1
T1_0 + T1_1 + T1_2 + T1_3 = 1
T2_0 + T2_1 + T2_2 + T2_3 = 1
T3_0 + T3_1 + T3_2 + T3_3 = 1
// We can only start a single task at a given time.
T0_0 + T1_0 + T2_0 + T3_0 = 1
T0_1 + T1_1 + T2_1 + T3_1 = 1
T0_2 + T1_2 + T2_2 + T3_2 = 1
T0_3 + T1_3 + T2_3 + T3_3 = 1
然后我们可以使用integer programming solver找到最佳组合来启动作业。
上面的模型是用这个(非常糟糕,但应该给你的想法)代码
生成的class Program
{
private static string[][] s_tasks = new string[][]
{
new string[] { "A", "B"},
new string[] { "B", "C"},
new string[] { "C", "D"},
new string[] { "E" }
};
static void Main(string[] args)
{
string filePath = Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), @"Desktop\lin_prog.txt");
using (TextWriter writer = new StreamWriter(filePath, false))
{
Console.SetOut(writer);
Console.WriteLine("Given tasks");
PrintTasks();
Console.WriteLine();
Console.WriteLine("Minimize:");
PrintObjectiveFunction();
Console.WriteLine();
Console.WriteLine("Subject to");
PrintConstraints();
}
}
static void PrintTasks()
{
for (int taskCounter = 0; taskCounter < s_tasks.Length; taskCounter++)
{
Console.WriteLine("Task {0}: [ {1} ]", taskCounter, String.Join(", ", s_tasks[taskCounter]));
}
}
static void PrintConstraints()
{
Console.WriteLine("// We start a task exactly once.");
for (int taskCounter = 0; taskCounter < s_tasks.Length; taskCounter++)
for (int timeCounter = 0; timeCounter < s_tasks.Length; timeCounter++)
{
Console.Write("T{0}_{1}", taskCounter, timeCounter);
if (timeCounter != s_tasks.Length - 1)
{
Console.Write(" + ");
}
else
{
Console.WriteLine(" = 1");
}
}
Console.WriteLine();
Console.WriteLine("// We can only start a single task at a given time.");
for (int timeCounter = 0; timeCounter < s_tasks.Length; timeCounter++)
for (int taskCounter = 0; taskCounter < s_tasks.Length; taskCounter++)
{
Console.Write("T{0}_{1}", taskCounter, timeCounter);
if (taskCounter != s_tasks.Length - 1)
{
Console.Write(" + ");
}
else
{
Console.WriteLine(" = 1");
}
}
}
static void PrintObjectiveFunction()
{
StringBuilder objective = new StringBuilder();
for (int currentTaskCounter = 0; currentTaskCounter < s_tasks.Length; currentTaskCounter++)
{
string[] currentTask = s_tasks[currentTaskCounter];
for (int otherTaskCounter = currentTaskCounter + 1; otherTaskCounter < s_tasks.Length; otherTaskCounter++)
{
string[] otherTask = s_tasks[otherTaskCounter];
if (ShouldPunish(currentTask, otherTask))
{
int maxPunishment = s_tasks.Length;
for (int currentTimeCounter = 0; currentTimeCounter < s_tasks.Length; currentTimeCounter++)
{
string currentTaskDecisionVar = String.Format("T{0}_{1}", currentTaskCounter, currentTimeCounter);
for (int otherTimeCounter = currentTimeCounter + 1; otherTimeCounter < s_tasks.Length; otherTimeCounter++)
{
string otherTaskDecisionVar = String.Format("T{0}_{1}", otherTaskCounter, otherTimeCounter);
// Punish tasks more in objective function if they are close in time when launched.
int punishment = maxPunishment - (otherTimeCounter - currentTimeCounter);
if (0 != objective.Length)
{
objective.Append(" + ");
}
objective.AppendFormat
(
"{0} * {1} * {2}",
punishment, currentTaskDecisionVar, otherTaskDecisionVar
);
}
objective.AppendLine();
}
}
}
}
// Nasty hack to align things.
Console.Write(" " + objective.ToString());
}
static bool ShouldPunish(string[] taskOne, string[] taskTwo)
{
bool shouldPunish = false;
foreach (string task in taskOne)
{
// We punish tasks iff. they need some of the same resources.
if(taskTwo.Contains(task))
{
shouldPunish = true;
break;
}
}
return shouldPunish;
}
}
需要注意的几件事
答案 1 :(得分:1)
我认为,如果我有一个解决问题的任意实例的方框,我可以提供它伪装的图形着色问题(http://en.wikipedia.org/wiki/Graph_coloring)并让它解决它们。我会将每个链接转换为链接任一侧的节点共享的资源。然后,可以将同时调度的所有节点着色为相同的颜色。因此,如果您的问题很容易,那么图形着色很简单,但图形着色是NP完全的,所以你被填充 - 好吧,差不多。
像寄存器分配这样的OTOH问题被简化为图形着色并在实践中大致解决,因此用于图形着色的方案之一也可能适用于您。参见例如http://en.wikipedia.org/wiki/Register_allocation答案 2 :(得分:-1)
除非你有清晰的层次结构,否则很难以编程方式强制执行。例如,您通常必须保留资源才能获得下一个资源。如果得到“B”你必须先拿“A”。要获得“C”,您必须同时持有“A”和“B”等。如果不是这种情况,那么我认为你能做的最好的事情就是写一个常用的例程,它总是以相同的顺序请求你的资源,然后是B然后是C等,并通过这个例程路由你的所有任务。理想情况下,我会在排队任务之前分配资源。
如果资源完全相同,则可以使用计数为5的信号量。例如,数据库连接池。但这似乎不是你的情况。