【问题标题】:How to parallelize Windows message loop and hook callback logic and the Ruby threads it creates?如何并行化 Windows 消息循环和挂钩回调逻辑以及它创建的 Ruby 线程?
【发布时间】:2009-08-16 22:05:30
【问题描述】:

我正在开发一个免费、简单、多平台的热键启动器工具,用 Ruby 编写。

现在我正在努力解决 Windows 实现的一些线程问题。 下面显示的粗略概念证明代码在我的日常使用中已经可靠地工作

HotkeyProcessor#process_keypress脚注 1)在按键被按下时被 C 代码调用并随后处理按键事件。

问题

但是,当 process_keypress 启动一个线程 执行一些耗时的工作(脚注 2)时,会出现一个细微的缺陷: 该程序将大部分时间花在 C 扩展中,等待 get_message脚注 3)返回——这在设计上永远不会发生。同时不会运行 Ruby 线程。 线程执行仅在 Ruby 级代码处于控制状态时发生,因此仅在 #process_keypress 处于活动状态时每个按键事件后的几毫秒内发生。

在这种情况下是否可以连续并发执行? 感谢您的任何建议!

require 'hotkey_processor'

def slowly_do_stuff
  5.times { |i| sleep(0.1); print "#{i} " }
end

module HotkeyProcessor
  def self.process_keypress(code, down) # footnote 1
    print '. '
    slowly_do_stuff              if code == 65 and down # A pressed
    Thread.new {slowly_do_stuff} if code == 66 and down # B pressed # footnote 2
    true
  end
end
puts 'Press A, B and other keys.'
HotkeyProcessor.start

热键处理器:

#include "ruby.h"
#include "windows.h"
#include <stdio.h>

int process_keypress_function;
VALUE HotkeyProcessor;
HHOOK keyboard_hook;

LRESULT CALLBACK callback_function(int Code, WPARAM wParam, LPARAM lParam)
{
  PKBDLLHOOKSTRUCT kbd = (PKBDLLHOOKSTRUCT)lParam;

  if (Code < 0 || Qtrue == rb_funcall(HotkeyProcessor, 
                                      process_keypress_function,
                                      2,
                                      INT2NUM(kbd->vkCode), /* code */
                                      (wParam == WM_KEYDOWN || 
                                       wParam == WM_SYSKEYDOWN) ? 
                                         Qtrue : Qfalse)) /* down */
  {
    return CallNextHookEx(keyboard_hook, Code, wParam, lParam);
  }
  else
    return 1;
}

static BOOL get_message(MSG *msg)
{
  return GetMessage(msg, 0, 0, 0);
}

static VALUE start(VALUE self)
{
    HMODULE Module = GetModuleHandle(NULL);
    MSG msg;

    keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, 
                                     (HOOKPROC) callback_function, 
                                     Module, 
                                     0);

    get_message(&msg); /* footnote 3 */

    /* Never get here. Wait forever for get_message() to return
       to keep the program alive and responding to callbacks. */
    return self;
}

static VALUE process_keypress(VALUE self, VALUE code)
{
  return Qnil;
}

void Init_hotkey_processor() {
  HotkeyProcessor = rb_define_module("HotkeyProcessor");
  rb_define_module_function(HotkeyProcessor, 
                            "process_keypress", 
                            process_keypress, 
                            1);
  rb_define_module_function(HotkeyProcessor, 
                            "start", 
                            start, 
                            0);
  process_keypress_function = rb_intern("process_keypress");
}

【问题讨论】:

    标签: c ruby multithreading winapi callback


    【解决方案1】:

    我不完全理解你在做什么,但是使用 PeekMessage 会返回怎么样?

    【讨论】:

      【解决方案2】:

      通过“rb_thread_blocking_region”启动 Windows 消息循环,并将“rb_thread_call_with_gvl”包裹在回调函数中发生的 Ruby API 调用中。

      查看工作演示git clone git://github.com/kaysarraute/ruby-win32-callback-threading.git


      /* Release the Giant VM Lock (GVL) while calling pump_messages */
      rb_thread_blocking_region(pump_messages, 0, 0, 0); 
      

      static BOOL pass_key_event_to_ruby(struct KEYEVENT *key_event) {
        /* Ruby API calls go here. */
      }
      
      /* In the callback: Reacquire GVL for calls to the Ruby API. */
      rb_thread_call_with_gvl(pass_key_event_to_ruby, &key_event);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-24
        相关资源
        最近更新 更多