一个用锁的场景,能否用clojure的并发机制取代

55 views
Skip to first unread message

Sun Ning

unread,
May 13, 2012, 11:14:13 AM5/13/12
to cn-cl...@googlegroups.com
我遇到这样一个场景,感觉clojure提供的机制比较难处理:

我有一个共享的netty客户端 channel,会有多个线程一起写,在每次write之前我
会检查他的状态,如果已经失效就需要重连。
在重连时,理想的情况是,只有一个线程真正执行重连任务,其他线程被block住
直到连接成功。

我找到2009年Stuart Halloway的一封邮件,提到的就是这个场景,当时似乎没有
人回答。

--
Sun Ning
Software developer
Nanjing, China (N32°3'42'' E118°46'40'')
http://about.me/sunng/bio

Hong Jiang

unread,
May 13, 2012, 11:22:02 AM5/13/12
to cn-cl...@googlegroups.com
用一个agent负责维护这个channel?

2012/5/13 Sun Ning <class...@gmail.com>:

Ruiyun Wen

unread,
May 13, 2012, 10:17:57 PM5/13/12
to cn-cl...@googlegroups.com
我觉得用agent还是有问题的。
想象一下代码,进行写操作时,应该只会用解引用,这样不会造成阻塞,而且应该也是逻辑所期望的。
那么,什么才应该引发阻塞呢,是重连。但重连代码是写在每次写之前,也就是说,agent会将这些重连任务排队,注意,是多次重连。我想这恐怕就不是期望的结果了。

由此引申一下,我发现用agent在事务中做最后的IO一击,好像是比较通行的惯用法,clojure in action也介绍了这种用法。但我在自己代码的几个场景下使用,都觉得挺别扭。举庄主博文关于agent写文件的例子,尽管说,我们可以认为写了文件,其背后的文件已经发生变化,用send将这个文件名重新赋予agent是合乎逻辑的。但如果同一个文件再写一次呢?agent保存的内容本身(文件名)其实并没有变化,但却仍然进行了一次赋值,直觉告诉自己这个味道不好。

我想,agent本身的设计初衷,应该还是要求其绑定的值是不可变的。然后通过send计算结果后重新绑定。这样才比较符合FP。但通过agent,确实可以串行化操作(比如IO),这个诱惑确实也让人比较凌乱。

总之,我的观点是,clojure目前提供的并发机制,最合理的应用场景仍然是处理内存集合。IO操作,如果需要的话,还是传统的锁更合适。

Robert Luo

unread,
May 14, 2012, 9:49:39 AM5/14/12
to cn-cl...@googlegroups.com
lock 是底层机制,clojure 提供了 locking 宏,本身说明锁机制仍然是同步机制之一。只不过对于大多数应用场景,象锁这样精细、容易出错的机制并非最佳,可以说其他同步机制是为希望避免精细化操作锁提供的更高层的抽象。

你的这个场景是否可以用 promise 来处理?检查线程进行重连,在重连成功后使用 deliver 函数将其他检查这个 promise 的线程解锁。例如:

(defn ready-channel [channel]
   (let [ch (promise)]
     (.start (Thread. #(do (check-and-reconnect channel) (deliver ch channel))))
     ch))
   
(defn safe-write [channel data]
   (when-let [ch (ready-channel channel)]
      (real-write @ch data)))

Sun Ning

unread,
May 14, 2012, 10:02:27 AM5/14/12
to cn-cl...@googlegroups.com, Robert Luo
promise 我考虑过,但是也有问题。
这种情况,需要多个线程等在同一个promise上,或者把connect重连成功的channel
deliver到每个线程的promise上。这两种方式在clojure里都有点难度。

确实这个场景可能用agent更好一点:
At any point in time, at most one action for each Agent is being
executed.
存在一个内在的同步机制,我以前没有注意到

dennis zhuang

unread,
May 14, 2012, 10:35:46 AM5/14/12
to cn-cl...@googlegroups.com, Robert Luo
agent也有问题,数量不好控制,因为每个action都是在线程池里执行,像网络可能阻塞的情况,一般会用send-off,而send-off的action会在一个没有限制大小的cache thread pool里执行,同时执行的action过多,可能线程过多,影响性能和内存状况。
--
庄晓丹
Email:        killm...@gmail.com xzh...@avos.com
Site:           http://fnil.net
Twitter:      @killme2008



naitong Xiao

unread,
May 14, 2012, 11:31:37 AM5/14/12
to cn-cl...@googlegroups.com
觉得还是直接用java的锁来的简单,double check一下应该性能不会有太大影响,毕竟重连不会太多吧
用Robert Luo提到的locking宏, 大致如下:

(let [reconnect-lock (Object.)]
  (defn reconnect [channel]
    (if (channel-closed? channel)
      (locking reconnect-lock
        (if (channel-closed? channel)
          (do-reconnect channel))))))

Ruiyun Wen

unread,
May 14, 2012, 9:26:58 PM5/14/12
to cn-cl...@googlegroups.com
(ns ^{:doc "place doc string here"
      :author "ruiyun"}
  tools.locking
  (:import [java.util.concurrent.locks ReentrantReadWriteLock]))

(defn rwlock
  "place doc string here"
  {:added "0.2.0"}
  []
  (ReentrantReadWriteLock.))

(defmacro rlocking
  "place doc string here"
  {:added "0.2.0"}
  [x & body]
  `(let [lockee# (.readLock ~x)]
     (try
       (.lock lockee#)
       ~@body
       (finally
         (.unlock lockee#)))))

(defmacro wlocking
  "place doc string here"
  {:added "0.2.0"}
  [x & body]
  `(let [lockee# (.writeLock ~x)]
     (try
       (.lock lockee#)
       ~@body
       (finally
         (.unlock lockee#)))))

这是我以前简单封装的读写锁,如需要供参考。呵呵,不过貌似对楼主问题不会有什么帮助。
另外,不知楼主是否考虑过应该把IO放到一个独立的工作线程中,其他线程通过消息队列和事件派发与其协同。在我的领域,大多数网络框架都是采用这样的模式设计,性能很高,而且还能大幅降低过多线程带来的复杂性。

Sun Ning

unread,
May 15, 2012, 4:58:41 AM5/15/12
to cn-cl...@googlegroups.com, Ruiyun Wen
我原先就是用locking加double check来做的,性能我想问题不大,就是代码有点不太美观
既然agent有个内在的同步机制我觉得应该是最好的方案了
> <mailto:xiaon...@gmail.com>>写道:
>
> 觉得还是直接用java的锁来的简单,double check一下应该性能不会有太大
> 影响,毕竟重连不会太多吧
> 用Robert Luo提到的locking宏, 大致如下:
>
> (let [reconnect-lock (Object.)]
> (defn reconnect [channel]
> (if (channel-closed? channel)
> (locking reconnect-lock
> (if (channel-closed? channel)
> (do-reconnect channel))))))
>
> 在 2012年5月14日 下午10:35,dennis zhuang <killm...@gmail.com
> <mailto:killm...@gmail.com>>写道:
>
> agent也有问题,数量不好控制,因为每个action都是在线程池里执
> 行,像网络可能阻塞的情况,一般会用send-off,而send-off的action
> 会在一个没有限制大小的cache thread pool里执行,同时执行的
> action过多,可能线程过多,影响性能和内存状况。
>
> 在 2012年5月14日 下午10:02,Sun Ning <class...@gmail.com
> <mailto:class...@gmail.com>>写道:
>
> promise 我考虑过,但是也有问题。
> 这种情况,需要多个线程等在同一个promise上,__或者把connect
> 重连成功的channel deliver到每个线程的promise上。__这两种方
> 式在clojure里都有点难度。
>
> 确实这个场景可能用agent更好一点:
> At any point in time, at most one action for each Agent is
> being executed.
> 存在一个内在的同步机制,我以前没有注意到
>
>
> On Mon 14 May 2012 09:49:39 PM CST, Robert Luo wrote:
>
> lock 是底层机制,clojure 提供了 locking 宏,本身说明锁
> 机制仍然是同步机
> 制之一。只不过对于大多数应用场景,象锁这样精细、__容易
> 出错的机制并非最
> 佳,__可以说其他同步机制是为希望避免精细化操作锁提供的
> 更高层的抽象__。
>
> 你的这个场景是否可以用 promise 来处理?检查线程进行重
> 连,在重连成功后
> 使用 deliver 函数将其他检查这个 promise 的线程解锁。例如:
>
> (defn ready-channel [channel]
> (let [ch (promise)]
> (.start (Thread. #(do (check-and-reconnect channel)
> (deliver ch
> channel))))
> ch))
> (defn safe-write [channel data]
> (when-let [ch (ready-channel channel)]
> (real-write @ch data)))
>
> On Sunday, May 13, 2012 11:14:13 PM UTC+8, Sun Ning wrote:
>
> 我遇到这样一个场景,感觉clojure提供的机制比较难处理:
>
> 我有一个共享的netty客户端 channel,会有多个线程一起
> 写,在每次write
> 之前我
> 会检查他的状态,如果已经失效就需要重连。
> 在重连时,理想的情况是,只有一个线程真正执行重连任
> 务,__其他线程被
> block住
> 直到连接成功。
>
> 我找到2009年Stuart Halloway的一封邮件,提到的就是这个
> 场景,当时似
> 乎没有
> 人回答。
>
> --
> Sun Ning
> Software developer
> Nanjing, China (N32°3'42'' E118°46'40'')
> http://about.me/sunng/bio
>
>
> --
> Sun Ning
> Software developer
> Nanjing, China (N32°3'42'' E118°46'40'')
> http://about.me/sunng/bio
>
>
>
>
> --
> 庄晓丹
> Email: killm...@gmail.com <mailto:killm...@gmail.com>
> xzh...@avos.com <mailto:xzh...@avos.com>

Sun Ning

unread,
May 15, 2012, 5:01:00 AM5/15/12
to cn-cl...@googlegroups.com, Ruiyun Wen
netty��writeӦ�þ��������ģ��
��������ǰ�򵥷�װ�Ķ�д������Ҫ���ο����Ǻǣ�����ò�ƶ�¥�����ⲻ����ʲô����
���⣬��֪¥���Ƿ��ǹ�Ӧ�ð�IO�ŵ�һ�������Ĺ����߳��У������߳�ͨ����Ϣ���к��¼��ɷ�����Эͬ�����ҵ����򣬴�������� ��ܶ��Dz��������ģʽ��ƣ����ܸܺߣ����һ��ܴ��͹���̴߳����ĸ����ԡ�

�� 2012��5��14�� ����11:31��naitong Xiao <xiaon...@gmail.com>д ����
���û���ֱ����java�������ļ򵥣�double checkһ��Ӧ�����ܲ�����̫��Ӱ�죬�Ͼ���������̫���
��Robert Luo�ᵽ��locking��, �������£�


(let [reconnect-lock (Object.)]
  (defn reconnect [channel]
    (if (channel-closed? channel)
      (locking reconnect-lock
        (if (channel-closed? channel)
          (do-reconnect channel))))))

�� 2012��5��14�� ����10:35��dennis zhuang <killm...@gmail.com>д ����

agent Ҳ�����⣬�������ÿ��ƣ���Ϊÿ��action�������̳߳���ִ�У��������������������һ�����send- off����send-off��action����һ��û�����ƴ�С��cache thread pool��ִ�У�ͬʱִ�е�action��࣬�����̹߳�࣬Ӱ�����ܺ��ڴ�״����

�� 2012��5��14�� ����10:02��Sun Ning <class...@gmail.com>д ����

promise �ҿ��ǹ���Ҳ�����⡣
�����������Ҫ����̵߳���ͬһ��promise�ϣ����߰�connect�����ɹ��� channel deliver��ÿ���̵߳�promise�ϡ������ַ�ʽ��clojure�ﶼ�� ���Ѷȡ�

ȷʵ�������������agent���һ�㣺

At any point in time, at most one action for each Agent is being executed.
����һ�����ڵ�ͬ�����ƣ�����ǰû��ע�⵽


On Mon 14 May 2012 09:49:39 PM CST, Robert Luo wrote:
lock �ǵײ���ƣ�clojure �ṩ�� locking �꣬����˵���������Ȼ��ͬ����
��֮һ��ֻ������ڴ����Ӧ�ó�������������ϸ�����׳���Ļ��Ʋ�����
�ѣ�����˵����ͬ��������Ϊϣ����⾫ϸ���������ṩ�ĸ�߲�ij���

�����������Ƿ������ promise �����?����߳̽����������������ɹ���
ʹ�� deliver ������������� promise ���߳̽������磺


(defn ready-channel [channel]
(let [ch (promise)]
(.start (Thread. #(do (check-and-reconnect channel) (deliver ch
channel))))
ch))
(defn safe-write [channel data]
(when-let [ch (ready-channel channel)]
(real-write @ch data)))

On Sunday, May 13, 2012 11:14:13 PM UTC+8, Sun Ning wrote:

   ����������һ���������о�clojure�ṩ�Ļ��ƱȽ��Ѵ��?

   ����һ�������netty�ͻ��� channel�����ж���߳�һ��д����ÿ��write
   ֮ǰ��
   �������״̬������Ѿ�ʧЧ����Ҫ������
   ������ʱ�����������ǣ�ֻ��һ���߳�����ִ���������������̱߳�
   blockס
   ֱ�����ӳɹ���

   ���ҵ�2009��Stuart Halloway��һ���ʼ����ᵽ�ľ��������������ʱ��
   ��û��
   �˻ش�


   --
   Sun Ning
   Software developer
   Nanjing, China (N32��3'42'' E118��46'40'')
   http://about.me/sunng/bio


--
Sun Ning
Software developer
Nanjing, China (N32��3'42'' E118��46'40'')
http://about.me/sunng/bio




--
ׯ����
Reply all
Reply to author
Forward
0 new messages