为可索引并发跳过列表实现交换方法(Implementing a swap method for an indexable concurrent skip list)
我正在实现一个基于Java的ConcurrentSkipListMap的并发跳过列表映射,区别在于我希望列表允许重复,并且我也希望列表是可索引的 (这样查找列表的第N个元素需要O(lg( n))时间,而不是像标准跳过列表那样的O(n)时间)。 这些修改不会产生问题。
另外,跳过列表的键是可变的。 例如,如果列表元素是整数{0,4,7},那么中间元素的键可以更改为[0,7]中的任何值,而不会提示更改列表结构; 如果密钥更改为(-inf,-1]或[8,+ inf),则删除该元素并重新添加以维护列表顺序。 除了在O(lg(n))插入后执行此操作外,我将其实现为一个删除,然后进行线性遍历,后跟一个O(1)插入(预期运行时间为O(1) - 99该节点将与相邻节点交换的时间百分比)。
插入一个全新的节点很少(在启动后),并且删除一个节点(不需要立即重新添加它)永远不会发生; 几乎所有的操作都是elementAt(i)来检索第i个索引处的元素,或者在键被修改后交换节点的操作。
我遇到的问题是如何实现关键修改类(es)。 从概念上讲,我想要做类似的事情
public class Node implements Runnable { private int key; private Node prev, next; private BlockingQueue<Integer> queue; public void update(int i) { queue.offer(i); } public void run() { while(true) { int temp = queue.take(); temp += key; if(prev.getKey() > temp) { // remove node, update key to temp, perform backward linear traversal, and insert } else if(next.getKey() < temp) { // remove node, update key to temp, perform forward linear traveral, and insert } else { key = temp; // node doesn't change position } } } }
(从
run
调用的insert
子方法使用CAS来处理两个节点试图同时插入同一位置的问题(类似于ConcurrentSkipListMap
处理冲突插入的方式) - 概念上,这与第一个节点锁定与插入点相邻的节点,除非在没有冲突的情况下减少开销。)通过这种方式,我可以确保列表总是按顺序进行的(如果密钥更新稍微延迟,可以确定更新最终会发生;但是,如果列表变得无序,那么事情可能会失灵)。 问题是用这种方式实现列表会产生很多线程,每个
Node
有一个线程(列表中有几千个节点) - 大多数线程在任何给定的时间点都会被阻塞,但是我担心有几个线程千个阻塞线程仍然会导致开销过高。另一种选择是使
update
方法同步并从Node
移除Runnable
接口,这样就不会让两个线程在Node
排队更新,然后在其单独的线程上处理这些更新,这两个线程将轮流执行Node#update
方法。 问题是这可能会造成瓶颈; 如果八个不同的线程都决定一次更新同一个节点,那么队列实现可以很好地扩展,但同步实现会阻塞八个线程中的七个(然后阻塞六个线程,然后是五个等)。所以我的问题是,除了线程数量减少之外,我将如何实现队列实现之类的东西,否则除非没有潜在的瓶颈问题,否则我将如何实现类似于同步实现的东西。
I'm implementing a concurrent skip list map based on Java's ConcurrentSkipListMap, the differences being that I want the list to allow duplicates, and I also want the list to be indexable (so that finding the Nth element of the list takes O(lg(n)) time, instead of O(n) time as with a standard skip list). These modifications aren't presenting a problem.
In addition, the skip list's keys are mutable. For example, if the list elements are the integers {0, 4, 7}, then the middle element's key can be changed to any value in [0, 7] without prompting a change to the list structure; if the key changes to (-inf, -1] or [8, +inf) then the element is removed and re-added to maintain the list order. Rather than implementing this as a removal followed by a O(lg(n)) insert, I implement this as a removal followed by a linear traversal followed by an O(1) insert (with an expected runtime of O(1) - 99% of the time the node will be swapped with an adjacent node).
Inserting a completely new node is rare (after startup), and deleting a node (without immediately re-adding it) never occurs; almost all of the operations are elementAt(i) to retrieve the element at the ith index, or operations to swap nodes after a key is modified.
The problem I'm running into is in how to implement the key modification class(es). Conceptually, I'd like to do something like
public class Node implements Runnable { private int key; private Node prev, next; private BlockingQueue<Integer> queue; public void update(int i) { queue.offer(i); } public void run() { while(true) { int temp = queue.take(); temp += key; if(prev.getKey() > temp) { // remove node, update key to temp, perform backward linear traversal, and insert } else if(next.getKey() < temp) { // remove node, update key to temp, perform forward linear traveral, and insert } else { key = temp; // node doesn't change position } } } }
(The
insert
sub-method being called fromrun
uses CAS in order to handle the problem of two nodes attempting to simultaneously insert at the same location (similar to how theConcurrentSkipListMap
handles conflicting inserts) - conceptually this is the same as if the first node locked the nodes adjacent to the insertion point, except that the overhead is reduced for the case where there's no conflict.)This way I can ensure that the list is always in order (it's okay if a key update is a bit delayed, because I can be certain that the update will eventually happen; however, if the list becomes unordered then things might go haywire). The problem being that implementing the list this way will generate an awful lot of threads, one per
Node
(with several thousand nodes in the list) - most of them will be blocking at any given point in time, but I'm concerned that several thousand blocking threads will still result in too high of an overhead.Another option is to make the
update
method synchronized and remove theRunnable
interface fromNode
, so that rather than having two threads enqueuing updates in theNode
which then takes care of processing these updates on its separate thread, the two threads would instead take turns executing theNode#update
method. The problem is that this could potentially create a bottleneck; if eight different threads all decided to update the same node at once then the queue implementation would scale just fine, but the synchronized implementation would block seven out of the eight threads (and would then block six threads, then five, etc).So my question is, how would I implement something like the queue implementation except with a reduced number of threads, or else how would I implement something like the synchronized implementation except without the potential bottleneck problem.
原文:https://stackoverflow.com/questions/16529799
最满意答案
运算符
==
和!=
不会比较类型。 因此,PHP会自动将'Hello'转换为0
(intval('Hello')
)的整数。 如果不确定类型,请使用类型比较运算符===
和!==
。 或者更好的确定你的程序在任何时候处理的是哪种类型。The operators
==
and!=
do not compare the type. Therefore PHP automatically converts 'Hello' to an integer which is0
(intval('Hello')
). When not sure about the type, use the type-comparing operators===
and!==
. Or better be sure which type you handle at any point in your program.
相关问答
更多-
运算符==和!=不会比较类型。 因此,PHP会自动将'Hello'转换为0 ( intval('Hello') )的整数。 如果不确定类型,请使用类型比较运算符===和!== 。 或者更好的确定你的程序在任何时候处理的是哪种类型。 The operators == and != do not compare the type. Therefore PHP automatically converts 'Hello' to an integer which is 0 (intval('Hello')). Wh ...
-
就像Raveren说的那样,你不能发送整数或布尔值到PHP。 所有发送的数据始终是字符串。 如果你仍然想使用if($ post)而不是if($ post =='true'),那么使用switch switch($post) { case "true": $post = true; break; case "false": $post = false; break; } if ($post) { ... } 没有其他办 ...
-
即使''似乎没什么,它仍然有一个值(字符串末尾的NULL字符)。 isset()检查变量是否设置,在这种情况下(到''),它是。 您可能希望先将$ ja设置为NULL,而不是将其设置为空字符串...或使用empty() ;) Even though '' seems like nothing, it still has a value (a NULL character at the end of the string). isset() checks if the variable is set or n ...
-
是的,因为它是具有潜在副作用的功能,它可能是真的。 例如: function carrier() { return $someValue++; } Yes, since it's a function with potential side effects, it might be true. For example: function carrier() { return $someValue++; }
-
如果找不到标记,则strpos()返回FALSE如果找到标记,则返回标记的(第一个)位置。 您需要使用严格比较运算符===检查布尔值FALSE以确定是否在字符串中找到了令牌: if(strpos(strtolower($msg),strtolower($token)) !== false){ echo 'ok'; } else { echo 'not ok'; } 这是因为PHP的宽松输入系统。 如果使用>=0 ,并且未找到标记,则PHP会在>=操作之前将strpos的FALSE返回值转换 ...
-
构造函数始终返回类的新实例 。 您不能从构造函数返回任何其他类型的值。 构建代码的更好方法是: source: signup.php class Signup { public $success; protected function buildProfile() { if($logic){ $this->success = true; }else{ $this->success = false; ...
-
您需要将$ email包裹到引号中,因为它是一个字符串。 所以: SELECT * FROM users WHERE email = '$email' You need to wrap $email into quotes because it is a string. So: SELECT * FROM users WHERE email = '$email'
-
使用WHERE子句。 ... WHERE status = 1 ORDER BY `id` DESC LIMIT 30 有关更多信息,请访问MySQL.com上的以下内容: https://dev.mysql.com/doc/refman/5.0/en/select.html 从手册: SELECT [ALL | DISTINCT | DISTINCTROW ] [HIGH_PRIORITY] [STRAIGHT_JOIN] [SQL_SMALL_RESULT] ...
-
这是预期的行为,如手册http://php.net/manual/en/types.comparisons.php中所述 Expression gettype() empty() is_null() isset() boolean ----------------------------------------------------------------------- $x = array(); array TRUE FALSE ...
-
你做错了。 .load()方法是异步的,不会从服务器返回值(它返回调用它的元素: $("#lvlupann") ),因此在if语句中使用返回的值没有意义。 您必须等待请求完成才能使用该值,如下所示: $("#lvlupann").load("/templ/checknlvl.php", function (value) { if (value == true) { $("#lvlupann").addClass("nlvlann"); } }); 至于PHP文件, retu ...